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

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

这个话题对我来说真的很有趣。在许多 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 -y

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

npm install express --save

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

npm install express-fileupload --save

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

在根目录中,创建一个index.js文件并添加以下代码片段。此文件设置 Web 服务器。 

var app = require('express')();
var fileUpload = require('express-fileupload');
var server = require('http').Server(app);
 
app.use(fileUpload()); 
app.get('/', function (req, res) {
  res.sendFile(__dirname + '/index.html');
});
server.listen(8080, () => {
  console.log('Server started on port 8080')
})

此文件导入 Express 和 Express File Upload 库,配置 Web 应用程序以使用 File Upload,并***端口 8080。它还在“/”处使用 Express 创建一个路由,这将是 Web 应用程序的默认登录页面。此路由返回一个index.html文件,其中包含允许用户上传 CSV 文件的 Web 表单。

您可以通过在终端中运行以下命令来启动 Web 服务器:

node index.js

您应该在端口 8080 上启动终端服务器中的消息。这意味着您已成功连接到 Web 服务器。

在根目录中,创建一个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 文件包含两个重要的东西:

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

  2. encType具有设置为的表单multipart/form-data和具有类型的输入字段file接受具有.csv 扩展名的文件。multipart/form-data当表单包含文件上传时, 这是一种在 HTML 中使用的编码方法。

当您访问http://localhost:8080/时,您将看到之前创建的表单。

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

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

npm install mongoose --save

创建模式和模型

安装 Mongoose 后,让我们创建一个新的author.js文件,该文件将定义作者模式和模型:

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 模板,我们将使用json2csv包。这个 npm 包将 JSON 转换为带有列标题和正确行尾的 CSV。

npm install json2csv --save

现在您将更新之前创建的index.js文件以包含/template的新路由。您会将模板路由的新代码附加到之前创建的index.js文件中

var template = require('./template.js');
app.get('/template', template.get);

这段代码做的第一件事是包含一个新的template.js文件(接下来要创建)并为/template创建一个路由。此路由将调用template.js文件get中的函数。

更新 Express 服务器以包含新路由后,让我们创建新的template.js文件:

var json2csv = require('json2csv').parse;
 
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);

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

创建 CSV 模板

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

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

此时,如果您要运行 Node 应用程序并在 Web 浏览器上导航到http://localhost:8080/,则 Web 表单将显示一个下载模板的链接。单击下载模板的链接将允许您下载authors.csv文件。此文件将在上传之前填充。

在 Microsoft Excel 中打开模板文件,CSV 文件应如下填充:

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

这是填充的 CSV 文件的示例

name.firstName,name.lastName,biography,twitter,facebook,linkedinMary,Doe,frontend developer,https://twitter.com/mary,https://www.facebook.com/mary,https://www.linkedin.com/maryMichael,Doe,backend developer,https://twitter.com/michael,https://www.facebook.com/michael,https://www.linkedin.com/michaelJohn,Doe,app developer,https://twitter.com/john,https://www.facebook.com/john,https://www.linkedin.com/john

此示例在上传后将创建三个作者。

拼图开始拼凑起来,形成一幅画。让我们来看看这个例子的主要内容并解析那个 CSV 文件。index.js文件需要一些更新才能连接到 MongoDB 并创建一个新的 POST 路由来接受文件上传:

var app = require('express')();
var fileUpload = require('express-fileupload');
var server = require('http').Server(app);
var template = require('./template.js');
var upload = require('./upload.js');
 
app.use(fileUpload());
 
app.get('/', function (req, res) {
  res.sendFile(__dirname + '/index.html');
});
app.get('/template', template.get);
app.post('/', upload.post);
 
mongoose.connect('mongodb://localhost/csvimport');
 
server.listen(8080, () => {
  console.log('Server started on port 8080')
})

配置数据库连接和新的 POST 路由后,就可以解析 CSV 文件了。幸运的是,有几个很棒的库可以帮助完成这项工作。对于本教程,我们将使用fast-csv可以通过以下命令安装的软件包:

npm install fast-csv --save

POST 路由的创建类似于从upload.js文件调用post函数的模板路由。不必将这些函数放在单独的文件中;但是,最好为这些路由创建单独的文件,因为它有助于保持代码的美观和有条理。

提交数据

最后,让我们创建包含在提交之前创建的表单时调用的函数的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属性来完成的。该属性与我在index.html示例file中首次定义的文件输入名称相匹配。

创建一个空authors数组,将在解析 CSV 文件时填充。该数组将用于将数据保存到数据库中。

fast-csv现在通过利用该fromString函数调用该库。此函数接受 CSV 文件作为字符串。字符串是从authorFile.data属性中提取的。该data属性包含上传的 CSV 文件的内容。

下一行包括该fast-csv函数的两个选项:headers和ignoreEmpty。这些都设置为true. 这告诉库 CSV 文件的第一行将包含标题,并且应该忽略空行。

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

该对象被更新为包含_id作者模式的属性,并带有一个新的ObjectId. 然后将该对象添加到authors数组中。

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

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

数据库架构应该类似于:

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

如果您想查看完整的源代码,可以查看包含代码的GitHub 存储库

结论

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

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


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