Ukrainian (українська мова) translation by AlexBioJS (you can also view the original English article)



Радий, що можу обговорити цю тему. У багатьох веб-додатках доволі часто відбувається отримання даних користувача та зберігання одного запису до вашої бази даних. Проте, як щодо того, коли ваші користувачі (чи ви самі) хочуть виконати масове вставлення однією командою?
Читайте статтю далі, щоб дізнатися, як створювати шаблон CSV (comma-separated values - значення, відокремлені комами) - файлу та форму для завантаження CSV - файлу та як виконати парсинг CSV - файлу для створення моделі Mongoose, яку збережемо до бази даних MongoDB.
Припускається, що ви маєте базові уявлення про Mongoose та її взаємодію з MongoDB. Якщо ж їх нема, то я раджу вам для початку прочитати мою статтю Вступ до Mongoose для MongoDB та Node.js. У ній повідомляється, як Mongoose взаємодіє з MongoDB способом створення строго-типізованих схем, з яких створюється модель. Якщо ви вже добре розумієте Mongoose, поїхали далі. У ній повідомляється, як Mongoose взаємодіє з MongoDB способом створення строго-типізованих схем, з яких створюється модель. Якщо ви вже добре розумієте Mongoose, поїхали далі.
Початок роботи
Для початку давайте створимо новий додаток Node.js. Перейдіть у командному рядку до директорії, куди ви хочете встановити ваш додаток Node.js, і виконайте наступні команди:
mkdir csvimport cd csvimport npm init
Я залишив значення за налаштуванням як є, тому мій додаток запуститься за допомогою файлу index.js. Спершу, до створення та парсингу CSV - файлів, нам необхідно виконати початкові налаштування. Я хочу створити веб-додаток. Для цього я збираюся доручити виконання всіх основних налаштувань сервера модулю Express. Для встановлення Express виконайте в консолі наступну команду:
npm install express --save
Оскільки наш веб-додаток буде приймати файли за допомогою веб-форми, я збираюся також використовувати підмодуль Express Express File Upload. Давайте тепер установимо і його:
npm install express-fileupload --save
Цієї початкової конфігурації достатньо, щоб установити мій веб-додаток і створити базову веб-сторінку з формою для завантаження файлів до сервера.
Нижче наводиться мій index.js
, у якому встановлюється мій веб-сервер:
var app = require('express')(); var fileUpload = require('express-fileupload'); var server = require('http').Server(app); app.use(fileUpload()); server.listen(80); app.get('/', function (req, res) { res.sendFile(__dirname + '/index.html'); });
У цьому прикладі ми підключаємо модулі Express та Express File Upload, налаштовуємо веб-додаток на використання File Upload та прослуховуємо підключення на 80 порту. Також у даному прикладі ми створили за допомогою Express маршрут для оброблення запитів до "/" , по якому буде відсилатися цільова сторінка за налаштуванням мого веб-додатку. У відповідь на запити по цьому шляху відправляється файл index.html
, у якому міститься веб-форма, що дозволяє користувачеві вивантажити CSV - файл. Оскільки додаток працює на моєму локальному комп'ютері, то у моєму випадку при переході до http://localhost я побачу форму, що створю в наступному прикладі.
Нижче наводиться моя сторінка index.html
з формою для вивантаження CSV - файлу:
<!DOCTYPE html> <html lang="en"> <head> <title>Upload Authors</title> </head> <body> <p>Use the form below to upload a list of authors. Click <a href="/template">here</a> for an example template.</p> <form action="/" method="POST" encType="multipart/form-data"> <input type="file" name="file" accept="*.csv" /><br/><br/> <input type="submit" value="Upload Authors" /> </form> </body> </html>
У цьому HTML-файлі є два важливі моменти:
- Посилання на "/template", при клацанні якого буде скачано шаблон CSV - файлу, який можна заповнити інформацією, що необхідно переслати.
- Форма, властивості
encType
якої надано значенняmultipart/form-data
, та поле для вводу типуfile
, що приймає файли з розширенням "csv".
Як ви могли помітити, HTML-файл посилається на шаблон Author. Якщо ви читали мою статтю 'Вступ до Mongoose...', то пам'ятаєте, що я створив схему Author. У цій статті я збираюся відтворити цю схему та створити для користувача можливість масового імпорту колекції авторів до моєї бази даних MongoDB. Давайте подивимося на схему Author. Проте, ви, певно, здогадались, що спочатку нам необхідно встановити модуль Mongoose:
npm install mongoose --save
Тепер, коли в нас встановлено Mongoose, давайте створимо новий файл author.js, у якому визначимо схему та модель Author:
Тепер, коли в нас встановлено Mongoose, давайте створимо новий файл author.js
, у якому визначимо схему та модель Author:
var mongoose = require('mongoose'); var authorSchema = mongoose.Schema({ _id: mongoose.Schema.Types.ObjectId, name: { firstName: { type: String, required: true }, lastName: String }, biography: String, twitter: { type: String, validate: { validator: function(text) { if (text !== null && text.length > 0) return text.indexOf('https://twitter.com/') === 0; return true; }, message: 'Twitter handle must start with https://twitter.com/' } }, facebook: { type: String, validate: { validator: function(text) { if (text !== null && text.length > 0) return text.indexOf('https://www.facebook.com/') === 0; return true; }, message: 'Facebook Page must start with https://www.facebook.com/' } }, linkedin: { type: String, validate: { validator: function(text) { if (text !== null && text.length > 0) return text.indexOf('https://www.linkedin.com/') === 0; return true; }, message: 'LinkedIn must start with https://www.linkedin.com/' } }, profilePicture: Buffer, created: { type: Date, default: Date.now } }); var Author = mongoose.model('Author', authorSchema); module.exports = Author;
Після створення схеми та моделі Author давайте переключимося та зосередимося на створенні шаблону CSV - файлу, який можна завантажити, клацнувши посилання шаблону. Для спрощення генерації шаблону CSV - файлу я збираюся використовувати модуль JSON (JavaScript Object Notation - текстовий формат обміну даними, заснований на JavaScript) to CSV. Давайте тепер установимо його:
npm install json2csv --save
Тепер я збираюся оновити мій заздалегідь створений файл index.js
, щоб додати новий маршрут для оброблення запитів до "/template":
var template = require('./template.js'); app.get('/template', template.get);
Я навів тільки новий код для маршруту шаблону, що додаємо до попереднього файлу index.js
.
У коді відбувається наступне: підключення нового файлу template.js
(буде створено наступним) і створення маршруту для оброблення запитів до "/template". При запитах до шляху цього маршруту буде викликано функцію get
у файлі template.js
.
Тепер, коли у нас оновлено сервер Express додаванням нового маршруту, давайте створимо новий файл template.js
:
var json2csv = require('json2csv'); exports.get = function(req, res) { var fields = [ 'name.firstName', 'name.lastName', 'biography', 'twitter', 'facebook', 'linkedin' ]; var csv = json2csv({ data: '', fields: fields }); res.set("Content-Disposition", "attachment;filename=authors.csv"); res.set("Content-Type", "application/octet-stream"); res.send(csv); };
У цьому файлі я спочатку підключаю заздалегідь установлений модуль json2csv
. Потім створюю та експортую функцію get
. Ця функція приймає в якості аргументів об'єкти запиту та відповіді від серверу Express.
Усередині функції я створив масив полів, які хочу використовувати у моєму шаблоні CSV - файлу. Створити список полів можна різними способами. Перший (який я використав у цьому прикладі) - це створення статичного списку полів, які необхідно включити до шаблону. Другий - це динамічне створення списку полів способом вибирання властивостей із схеми Author.
Другий спосіб можна було б виконати за допомогою наступного коду:
var fields = Object.keys(Author.schema.obj);
Я б захотів використовувати цей динамічний метод, якщо б не труднощі, які виникають, якщо я не хочу включати безліч властивостей із схеми до шаблону CSV - файлу. У моєму прикладі у шаблоні не використовуються властивості _id
і created
, оскільки їх значення будуть задані за допомогою коду. Однак, якщо у вас немає полів, котрі ви хочете вилучити, то динамічний спосіб також підходить.
Створення шаблону CSV - файлу
Після визначення масиву полів я використовую модуль json2csv
для створення із об'єкта JavaScript шаблону файлу. Цей об'єкт csv
буде результатом запитів до маршруту зі шляхом '/template'.
І, нарешті, використовуючи властивість res
сервера Express, я встановлюю значення двох заголовків, що забезпечить завантажування файлу authors.csv
.
Якщо б ви запустили ваш Node-додаток зараз і перейшли в своєму веб-браузері за посиланням http://localhost, то на екрані відобразилась би веб-форма з посиланням для завантаження шаблону. Клацнувши посилання для завантаження шаблону, ви завантажите файл authors.csv
для заповнення перед вивантаженням.
Нижче наводиться приклад заповненого CSV - файлу:
name.firstName,name.lastName,biography,twitter,facebook,linkedin Jamie,Munro,Jamie is a web developer and author,,, Mike,Wilson,Mike is a web developer and Node.js author,,,
Результатом вивантаження цього шаблону буде створення двох авторів: мене та мого друга, який написав книгу про Node.js декілька років тому. Ви, скоріш за все, помітили, що в кінці кожного рядка розташовані три коми ",,,". Це зроблено задля скорочення прикладу. Я не заповнив властивості соціальних мереж (twitter
, facebook
і linkedin
).
Кусочки мозаїки починають збиратися докупи та формувати загальну картину. Давайте перейдемо до найважливішого нашого прикладу та виконаємо парсинг CSV - файлу. Нам необхідно трохи оновити файл index.js
для підключення до MongoDB і створення нового маршруту для оброблення запитів POST, по якому до сервера завантажується файл.
var app = require('express')(); var fileUpload = require('express-fileupload'); var mongoose = require('mongoose'); var server = require('http').Server(app); app.use(fileUpload()); server.listen(80); mongoose.connect('mongodb://localhost/csvimport'); app.get('/', function (req, res) { res.sendFile(__dirname + '/index.html'); }); var template = require('./template.js'); app.get('/template', template.get); var upload = require('./upload.js'); app.post('/', upload.post);
Після підключення до бази даних і налаштування нового маршруту для оброблення запитів POST прийшов час виконати парсинг CSV - файлу. На щастя, для цього є декілька відмінних бібліотек. Я вирішив використовувати модуль fast-csv
, який ви можете встановити за допомогою наступної команди:
npm install fast-csv --save
Маршрут для оброблення запитів POST було створено подібно до маршруту шаблону. У ньому викликається функція post
із файлу upload.js
. Нема необхідності розміщати функції маршрутів до різних файлів. Однак, мені хочеться зробити різні файли для цих маршрутів, оскільки це допомагає підтримувати читабельність і організованість коду.
Відправлення даних
І, нарешті, давайте створимо файл upload.js
, в якому міститься функція post
, яку викликаємо після відправки заздалегідь створеної форми:
var csv = require('fast-csv'); var mongoose = require('mongoose'); var Author = require('./author'); exports.post = function (req, res) { if (!req.files) return res.status(400).send('No files were uploaded.'); var authorFile = req.files.file; var authors = []; csv .fromString(authorFile.data.toString(), { headers: true, ignoreEmpty: true }) .on("data", function(data){ data['_id'] = new mongoose.Types.ObjectId(); authors.push(data); }) .on("end", function(){ Author.create(authors, function(err, documents) { if (err) throw err; }); res.send(authors.length + ' authors have been successfully uploaded.'); }); };
Доволі багацько відбувається в цьому файлі. У перших трьох рядках ми підключаємо необхідні модулі, які знадобляться для здійснення парсингу та зберігання даних CSV - файлу.
Далі визначається та експортується функція post
для використання у файлі index.js
. Саме усередині цієї функції відбувається чарування.
Спочатку функція перевіряє наявність файлу в тілі запиту. Якщо файлу нема, тоді повертається помилка, яка вказує, що необхідно вивантажити файл.
Коли файл вивантажено, посилання на файл зберігається до змінної з іменем authorFile
завдяки звертанню до масиву files
і властивості file
цього масиву. Ім'я властивості file
відповідає імені поля для відправки файлу, яке я зазначив у прикладі index.html
.
Також я створив масив authors
, який буде наповнюватися поки виконується парсинг CSV - файлу. Цей масив буде використовуватися для зберігання даних до бази даних.
Далі викликається бібліотека fast-csv
під час використання її функції fromString
. Функція приймає CSV - файл у вигляді рядка. Я вибрав рядок із властивості authorFile.data
. Властивість data
містить вміст мого вивантаженого CSV - файлу.
Я передав дві опції до функції fromString модуля fast-csv
: headers
і ignoreEmpty
. Значеннями обох є true
. Це повідомляє бібліотеці, що в першому рядку CSV - файлу буде міститися заголовки і що необхідно ігнорувати порожні рядки.
Тепер, коли опції налаштовано, я визначив дві функції обробки подій, що викликаються при генеруванні подій data
и end
. Подія data
генерується раз для кожного рядка CSV - файлу. У цій події міститься об'єкт JavaScript даних, що піддалися парсингу.
Я оновлюю цей об'єкт для включення властивості _id
схеми Author з новим значенням типу ObjectId
(унікальний ідентифікатор об'єкта, первинний ключ, _id). Далі цей об'єкт додається до масиву authors
.
Після того, як відбувся парсинг усього CSV - файлу, генерується подія end
. Усередині функції зворотного виклику для цієї події я викликаю функцію create
моделі Author, передаючи їй масив authors
.
Якщо під час зберігання масиву відбувається помилка, то викидається виняток. В іншому випадку користувачеві відображується повідомлення про кількість авторів, вивантажених і збережених до бази даних.
На випадок, якщо ви б хотіли побачити весь код, я створив репозиторій GitHub з кодом.
Завершення
У моєму прикладі я вивантажив тільки декілька записів. Якщо у вашому випадку необхідне вивантаження тисяч записів, то було б непогано зберігати записи невеликими частинами.
Це можна здійснити різними способами. Якщо б мені потрібно було це реалізувати, то я б запропонував оновити функцію зворотного виклику, зареєстровану на подію data
, додавши перевірку довжини масиву authors. У випадку перевищення довжини масиву встановленої вами довжини, наприклад, 100, викличте функцію Author.create
для цього масиву і потім скиньте (присвойте значення 0) масив. Потім функція збереже записи частинами по 100. Пересвідчіться, що залишили останній виклик функції create
у функції зворотного виклику, зареєстрованій на подію end
, для зберігання кінцевих записів.
Тіштеся!
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weekly