Эта тема очень приятная для меня. Во многих веб-приложениях довольно часто принимают пользовательский ввод и сохраняют одну запись в вашей базе данных. Но что делать, когда ваши пользователи (или вы) хотят выполнить несколько вставок в одной команде?
Войдите в эту статью, которая покажет, как создать шаблон CSV и форму для загрузки файла CSV, и как проанализировать CSV в модели Mongoose, которая будет сохранена в базе данных MongoDB.
В этой статье предполагается, что у вас есть базовое понимание Mongoose и того, как он взаимодействует с MongoDB. Если вы этого не сделаете, я бы посоветовал сначала прочитать мою статью « Введение в Mongoose для MongoDB» и «Node.js» . В этой статье описывается, как Mongoose взаимодействует с MongoDB путем создания строго типизированных схем, из которых создается модель. Если у вас уже есть хорошее понимание Mongoose, давайте продолжим.
Начиная
Для начала давайте создадим новое приложение Node.js. В командной строке перейдите туда, где вы хотите разместить свои приложения Node.js, и выполните следующие команды:
1
2
3
|
mkdir csvimport
cd csvimport
npm init
|
Я оставил все настройки по умолчанию, поэтому мое приложение будет запускаться с index.js
. Перед созданием и анализом CSV-файлов сначала необходимо выполнить некоторые начальные настройки. Я хочу сделать это веб-приложением; чтобы сделать это, я собираюсь использовать пакет Express, чтобы обработать все настройки сервера. В командной строке установите Express, выполнив следующую команду:
1
|
npm install express —save
|
Поскольку это веб-приложение будет принимать файлы через веб-форму, я также собираюсь использовать подпакет Express Express File Upload. Давайте установим это и сейчас:
1
|
npm install express-fileupload —save
|
Теперь я выполнил достаточно начальной настройки, чтобы настроить мое веб-приложение и создать базовую веб-страницу, которая будет создавать форму для загрузки моего файла.
Вот мой файл index.js
который устанавливает мой веб-сервер:
01
02
03
04
05
06
07
08
09
10
11
|
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, настраивается мое веб-приложение для использования загрузки файлов и прослушивается порт 80. В этом примере также был создан маршрут с использованием Express в «/», который будет целевой страницей по умолчанию для моего веб-сайта. заявление. Этот маршрут возвращает файл index.html
который содержит веб-форму, которая позволит пользователю загрузить файл CSV. В моем случае я работаю на своем локальном компьютере, поэтому при посещении http: // localhost я увижу форму, которую я создаю в следующем примере.
Вот моя страница index.html
которая создает мою форму для загрузки файла CSV:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<!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», я создал схему автора. В этой статье я собираюсь воссоздать эту схему и позволить пользователю массово импортировать коллекцию авторов в мою базу данных MongoDB. Давайте посмотрим на схему автора. Однако, прежде чем мы это сделаем, вы, наверное, догадались — нам нужно установить пакет Mongoose:
1
|
npm install mongoose —save
|
Создание схемы и модели
С установленным Mongoose, давайте создадим новый файл author.js
, который определит схему и модель автора:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
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;
|
С созданной авторской схемой и моделью давайте переключимся и сосредоточимся на создании шаблона CSV, который можно скачать, нажав на ссылку шаблона. Чтобы помочь с генерацией шаблона CSV, я собираюсь использовать пакет JSON to CSV. Давайте установим это сейчас:
1
|
npm install json2csv —save
|
Сейчас я собираюсь обновить свой ранее созданный файл index.js
чтобы включить новый маршрут для «/ template»:
1
2
|
var template = require(‘./template.js’);
app.get(‘/template’, template.get);
|
Я включил только новый код для шаблона маршрута, который добавлен к предыдущему файлу index.js
.
Первое, что делает этот код, это включает новый файл template.js
(который будет создан далее) и создает маршрут для «/ template». Этот маршрут вызовет функцию get
в файле template.js
.
Обновив сервер Express, добавив новый маршрут, давайте создадим новый файл template.js
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
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. Это можно сделать одним из двух способов. Первый способ (это делается в этом примере) — создать статический список полей, которые будут включены в шаблон. Второй способ — это динамическое создание списка полей путем извлечения свойств из схемы автора.
Второй способ можно сделать с помощью следующего кода:
1
|
var fields = Object.keys(Author.schema.obj);
|
Мне бы хотелось использовать этот динамический метод, но он становится немного сложнее, когда я не хочу включать несколько свойств из схемы в мой шаблон CSV. В этом случае мой шаблон не включает _id
и created
свойства, потому что они будут заполнены с помощью кода. Однако, если у вас нет полей, которые вы хотите исключить, динамический метод также будет работать.
Создание шаблона CSV
Определив массив полей, я использую пакет json2csv
для создания моего шаблона CSV из моего объекта JavaScript. Этот объект csv
будет результатом этого маршрута.
И наконец, используя свойство res
с сервера Express, я установил два свойства заголовка, которые будут принудительно загружать файл authors.csv
.
На этом этапе, если вы запустите приложение Node и перейдете по адресу http: // localhost в своем веб-браузере, появится веб-форма со ссылкой для загрузки шаблона. Нажав на ссылку для загрузки шаблона, вы сможете загрузить файл authors.csv
который будет заполнен до его загрузки.
Вот пример заполненного файла CSV:
1
2
3
|
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, который будет принимать загрузку файла:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
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
, который можно установить с помощью следующей команды:
1
|
npm install fast-csv —save
|
Маршрут POST был создан аналогично маршруту шаблона, который вызывает функцию post
из файла upload.js
. Нет необходимости размещать эти функции в отдельных файлах; Тем не менее, мне нравится создавать отдельные файлы для этих маршрутов, так как это помогает поддерживать красивый и организованный код
Отправка данных
И наконец, давайте создадим файл upload.js
который содержит функцию post
которая вызывается при upload.js
ранее созданной формы:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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
функции fromString
. Эта функция принимает файл CSV в виде строки. Я извлек строку из свойства authorFile.data
. Свойство data
содержит содержимое моего загруженного файла CSV.
Я включил два параметра в функцию fast-csv
: headers
и ignoreEmpty
. Они оба установлены в true
. Это говорит библиотеке, что первая строка CSV-файла будет содержать заголовки и что пустые строки следует игнорировать.
С настроенными параметрами я настроил две функции прослушивателя, которые вызываются, когда происходит событие data
событие end
. Событие data
вызывается один раз для каждой строки файла CSV. Это событие содержит объект JavaScript проанализированных данных.
Я обновляю этот объект, чтобы включить свойство ObjectId
автора с новым ObjectId
. Этот объект затем добавляется в массив authors
.
Когда файл CSV был полностью проанализирован, запускается событие end
. Внутри функции обратного вызова события я вызываю функцию create
для модели Author, передавая ей массив authors
.
Если при попытке сохранить массив возникает ошибка, генерируется исключение; в противном случае пользователю отображается сообщение об успехе, указывающее, сколько авторов было загружено и сохранено в базе данных.
Если вы хотите увидеть полный исходный код, я создал GitHub-репозиторий с кодом.
Вывод
В моем примере я загрузил только пару записей. Если в вашем случае использования требуется возможность загружать тысячи записей, было бы неплохо сохранить записи небольшими порциями.
Это можно сделать несколькими способами. Если бы я это реализовал, я бы предложил обновить функцию обратного вызова data
чтобы проверить длину массива авторов. Когда массив превышает заданную вами длину, например, 100, вызовите функцию Author.create
для массива, а затем сбросьте массив на пустой. После этого записи будут сохраняться кусками по 100. Обязательно оставьте последний вызов create
в функции обратного вызова end
для сохранения окончательных записей.
Наслаждайтесь!