• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

使用Mongoose和Node.js将CSV文件批量导入MongoDB

使用Mongoose和Node.js将CSV文件批量导入MongoDB  第1张您将要创建 的内容

这个话题对我来说真的很有趣。在许多 Web 应用程序中,接受用户输入并将单个记录保存到数据库中是很常见的。但是当您的用户(或您)想要在一个命令中执行多个插入时呢? 

进入这篇文章,它将演示如何创建一个csv模板和一个表单来上传 CSV 文件,以及如何将 CSV 解析为 mongoose 模型,并将其保存到mongodb数据库中。

本文假设您对 Mongoose 以及它如何与 MongoDB 交互有基本的了解。如果您不这样做,我建议您先阅读我的 Mongoose 简介 for MongoDB 和 node.js 文章。本文描述了 Mongoose 如何通过创建强类型模式来与 MongoDB 交互,模型是从该模式中创建的。如果您已经对 Mongoose 有很好的了解,那么让我们继续。

入门

首先,让我们实例化一个新的 Node.js 应用程序。在命令提示符中,导航到要托管 Node.js 应用程序的位置并执行以下命令:

mkdir csvimport
cd csvimport
npm init

我保留了所有默认设置,所以我的应用程序将以index.js在创建和解析 CSV 文件之前,需要先进行一些初始设置。我想让它成为一个网络应用程序;为此,我将使用 Express 包来处理所有细节的服务器设置。在命令提示符下,通过运行以下命令安装 Express:

npm install express --save

由于此 Web 应用程序将通过 Web 表单接受文件,因此我还将使用 Express 子包 Express File Upload。让我们现在也安装它:

npm install express-fileupload --save

我现在已经完成了足够的初始配置来设置我的 Web 应用程序并创建一个基本网页,该网页将创建我的文件上传表单。

这是我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 库,配置我的 Web 应用程序以使用 File Upload,并***端口 80。此示例还在“/”处使用 Express 创建了一个路由,这将是我的 Web 的默认登录页面应用。此路由返回一个index.html文件,其中包含允许用户上传 CSV 文件的 Web 表单。就我而言,我在本地计算机上运行,所以当我访问 http://localhost时 ,我将看到我在下一个示例中创建的表单。

这是我index.html创建用于上传 CSV 文件的表单的页面:

<!DOCTYPE html> <html> <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 文件包含两个重要的东西:

  1. 单击“/模板”的链接将下载一个 CSV 模板,该模板可以填充要导入的信息。

  2. encType具有设置为的表单multipart/form-data和具有类型的输入字段file接受具有“csv”扩展名的文件。

您可能已经注意到,HTML 引用了作者模板。如果您阅读我的猫鼬简介文章,我创建了一个作者模式。在本文中,我将重新创建此 Schema 并允许用户将作者集合批量导入我的 MongoDB 数据库。让我们看一下作者模式。不过,在我们这样做之前,您可能已经猜到了——我们需要安装 Mongoose 包:

npm install mongoose --save

创建模式和模型

安装 Mongoose 后,让我们创建一个新author.js文件来定义 Author Schema 和 Model:

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 Schema 和 Model 后,让我们换个思路,专注于创建 CSV 模板,单击模板链接即可下载该模板。为了帮助生成 CSV 模板,我将使用 JSON 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 模板中的字段数组。这可以通过以下两种方式之一来完成。第一种方法(在此示例中完成)是创建要包含在模板中的字段的静态列表。第二种方法是通过从作者模式中提取属性来动态创建字段列表。

第二种方法可以使用以下代码完成:

var fields = Object.keys(Author.schema.obj);

我本来希望使用这种动态方法,但是当我不想将架构中的多个属性包含到我的 CSV 模板中时,它会变得有点复杂。在这种情况下,我的模板不包含_idandcreated 属性,因为这些将通过代码填充。但是,如果您没有要排除的字段,动态方法也可以使用。

创建 CSV 模板

定义了字段数组后,我使用json2csv包从我的javascript对象创建我的 CSV 模板。这个csv对象将是这条路线的结果。

最后,使用resExpress 服务器的属性,我设置了两个标题属性,将强制下载authors.csv文件。

此时,如果您要运行 Node 应用程序并在 Web 浏览器中导航到http://localhost  ,则 Web 表单将显示一个下载模板的链接。单击下载模板的链接将允许您在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 的书的朋友。您可能会注意到,每行的末尾是三个逗号“,,,”。这样做是为了简化示例。我没有填充社交网络属性(twitterfacebooklinkedin)。

拼图开始拼凑起来,形成一幅画。让我们来看看这个例子的主要内容并解析那个 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 文件的内容。

我在该fast-csv函数中包含了两个选项:headersignoreEmpty这些都设置为true这告诉库 CSV 文件的第一行将包含标题,并且应该忽略空行。

配置好选项后,我设置了两个监听函数,当data 事件end事件被触发时调用。data对于 CSV 文件的每一行,都会调用一次该事件。此事件包含已解析数据的 JavaScript 对象。

我更新了这个对象以包含_idAuthor Schema 的属性和一个新的ObjectId然后将该对象添加到authors数组中。

当 CSV 文件被完全解析后,end事件被触发。在事件回调函数内部,我调用createAuthor 模型上的函数,将数组传递authors给它。

如果尝试保存数组时发生错误,则会引发异常;否则,将向用户显示一条成功消息,指示有多少作者已上传并保存到数据库中。

如果您想查看完整的源代码,我已经使用该代码创建了一个GitHub 存储库

结论

在我的示例中,我只上传了几条记录。如果您的用例需要能够上传数千条记录,那么将记录保存在较小的块中可能是个好主意。

这可以通过多种方式完成。如果我要实现它,我建议更新data回调函数以检查作者数组的长度。当数组超过您定义的长度时,例如 100,调用Author.create数组上的函数,然后将数组重置为空。然后,这会将记录保存为 100 个块。确保将最终create调用留在end回调函数中以保存最终记录。


文章目录
  • 入门
  • 创建模式和模型
  • 创建 CSV 模板
  • 提交数据
  • 结论