如何导出csv nodejs

Posted

技术标签:

【中文标题】如何导出csv nodejs【英文标题】:How to export csv nodejs 【发布时间】:2013-08-20 18:57:54 【问题描述】:

嘿,我正在尝试从 node.js 导出 csv(从 mongodb 中提取数据)。我已经将数据拉出并用逗号分隔,但现在我正试图弄清楚如何实际发送它......我将此代码粘贴在我的路由文件中。关于如何获取数据数组并将其直接发送给用户以供下载的任何建议。

这里是代码:(我尝试了代码底部的第二个函数)

exports.downloadContacts = function(req, res) 
    async.waterfall([
        function(callback) 
            var source = [];
            Friend.find(userId: req.signedCookies.userid, function(err, friends) 
                if(err) console.log('err with friends for download');
                 else 
                    var userMap = ;
                    var friendIds = friends.map(function (user) 
                        userMap[user.friend_id] = user;
                        return user.friend_id;
                    );
                    console.log(friends);
                    User.find(_id: $in: friendIds, function(err, users) 
                        if(err) console.log(err); 
                         else 
                            for(var i = 0; i < users.length; i++) 
                                console.log('users')
                                //console.log(users[i]);
                                source.push(users[i].firstNameTrue, users[i].lastNameTrue, users[i].emailTrue, users[i].phone, users[i].emailList, users[i].phoneList)
                            
                            console.log(source);
                            callback(null, source);
                        


                    );
                


            );

        
    ],
    function(err, source) 
        var result = [];

        res.contentType('csv');

        csv()
        .from(source)
        .on('data', function(data) 
            result.push(data.join());
        )
        .on('end', function()
            res.send(result.join('\n'));
        );
    );     
;

【问题讨论】:

【参考方案1】:

我不完全确定您的问题是什么,但我认为您要查找的内容可以通过设置 Content Disposition 标头来解决。看看这个关于另一个 SO 问题的回复:https://***.com/a/7288883/2320243。

【讨论】:

我正在尝试离线发送用户的好友列表,让他们将好友列表下载为由 mongodb 形成的 csv,并直接发送给用户进行下载,而无需将其下载到我们的系统...这没有帮助..【参考方案2】:

您是否尝试过类似的内容类型为“application/octet-stream”

res.set('Content-Type', 'application/octet-stream');
res.send(<your data>);

或者干脆

res.send(Buffer.from(<your data>));

Express send() docs.

【讨论】:

是的,可以,但是如何正确设置内容类型...是否应该在我的下载文件夹中显示为类型-文件?还是有更友好的发送方式?这可以正常工作并且可以使用任何应用程序打开,但只是想知道是否有更好的方法来设置类型或如何使它看起来更好,或者如果您尝试发送文件,人们会这样做吗?跨度> “更好”是什么意思?也许是“文本/纯文本”?你能给我更多关于你想要做什么的信息吗?干杯。 不确定你真正需要什么,但我可以帮助你expressjs.com/api.html#res.download或expressjs.com/api.html#res.sendfile不确定你是否可以发送缓冲区而不是文件,也许你可以直接发送而不保存它到FS。这将提示客户端下载文件。希望对您有所帮助。 在这种情况下我们必须发送什么类型的数据??数组还是 JSON? new Buffer 已弃用,请改用Buffer.from【参考方案3】:

Express-csv 是一个很棒的模块,用于将 csv 内容写入来自 node.js 服务器的流,它将作为响应发送给客户端(并作为文件下载)。非常容易使用。

app.get('/', function(req, res) 
  res.csv([
    ["a", "b", "c"]
  , ["d", "e", "f"]
  ]);
);

文档:https://www.npmjs.com/package/express-csv

当你传递一个对象时,你需要显式地添加标题(如果你想要的话)。这是我使用npm mysql的示例

router.route('/api/report')
    .get(function(req, res) 
            query = connection.query('select * from table where table_id=1;', function(err, rows, fields) 
                if (err) 
                    res.send(err);
                
                var headers = ;
                for (key in rows[0]) 
                    headers[key] = key;
                
                rows.unshift(headers);
                res.csv(rows);
            );
    );

【讨论】:

如何从上面的代码(将 csv 对象附加到响应)到提供用户可以下载的文件? 我现在明白了,这就是提供下载所需的全部内容。很甜。 我无法发送包含一百万行数据的行。有没有办法处理这个【参考方案4】:

这是我所做的:

    使用json2csv从mongodb数据构建csv数据

var json2csv = require('json2csv');
var fields = ['name', 'phone', 'mobile', 'email', 'address', 'notes'];
var fieldNames = ['Name', 'Phone', 'Mobile', 'Email', 'Address', 'Notes'];
var data = json2csv( data: docs, fields: fields, fieldNames: fieldNames );
    向客户端发送 csv 数据

res.attachment('filename.csv');
res.status(200).send(data);

【讨论】:

你如何在前端使用这个? @Ohjay44 我使用 express js 框架将它用于我的前端 (FE) 网站。如果把 API 和 FE 分开,我觉得 FE 和 API 应该通过 json 一起通信。您将把这个 json 数据导出到 FE 上的 csv。我是否正确理解了您的问题? 事实证明,一旦我收到 javascript 中的数据,我需要做的就是触发它作为文件下载。我收到的只是原始格式的文件,所以我很困惑如何在我的通话完成后开始下载 csv 文件。 @VietTran 会在服务器上创建文件以防它部署在服务器上吗?如果有多个下载请求,我不希望服务器使用上述解决方案创建太多文件。 @y_159 我在nodejs上工作已经太久了,但是没有,上面的代码没有在服务器上创建任何文件。变量data 仅在内存中。【参考方案5】:

根据@VierTD 的回答,您必须使用 json2csv 从 mongodb 数据构建 csv 数据。

使用依赖项更新 Package.json:

  "dependencies": 
    "mongodb": "^2.2.10",
    "json2csv": "*",
    "express": "*"
  

创建 json2CSV 对象,记住这是映射您的文档(数据库表),因此“字段”上的名称必须与数据库表匹配,字段名称由您决定,但也很重要,因为这些是 CSV 文件列名称:

var json2csv = require('json2csv');
var fields = ['name', 'phone', 'mobile', 'email', 'address', 'notes'];
var fieldNames = ['Name', 'Phone', 'Mobile', 'Email', 'Address', 'Notes'];
var data = json2csv( data: docs, fields: fields, fieldNames: fieldNames );

向客户端发送csv数据:

res.attachment('filename.csv');
res.status(200).send(data);

This code展示了如何基于mongo db文档(数据库表)导出csv文件

我已经创建了一个github repo 非常简短的示例,还在Mongo Lab Website 创建了一个包含虚拟数据的数据库,因此此代码将立即在您的计算机中运行。基于@VietTD 的答案。

如果您有兴趣测试此代码,请记住更改以下行。

更改数据库 url(使用您自己的数据库这可行,但仅用于显示目的)。

        var url = 'mongodb://admin:detroit123@ds063946.mlab.com:63946/misale_dev';

更改目标文档(或数据库表)。

var collection = db.collection('_dummy');

更改文档列(或 db 表的列字段):

var fields = ['_id', 'JobID', 'LastApplied'];

最后设置你的 CSV 标题列名以及你的 CSV 文件名:

var fieldNames = ['ID_OR_PK', 'JOB_UNIQUE_ID_TITLE', 'APPLICATION_DATE'];

最后但并非最不重要的一点:

res.attachment('yourfilenamehere.csv');

请随时改进示例代码,我将不胜感激!或者只是下载它并查看代码。它已经带有一个数据库,所以它很容易理解和运行,在这种情况下你不需要关心这里是整个代码,也许你会看到一些有趣的东西:

//
// EXPRESS JS SERVER INITI
//
var express = require('express')
var app = express()

//
// MONGO DB INIT
//
var MongoClient = require('mongodb').MongoClient, assert = require('assert');
app.get('/', function (req, res) 
    var url = 'mongodb://admin:detroit123@ds063946.mlab.com:63946/misale_dev';
    //
    // This function should be used for migrating a db table to a TBD format
    //
    var migrateMongoDBTable = function(db, callback) 
        // Get the documents collection
        console.log("Reading database records");
        // Get the documents collection
        var collection = db.collection('_dummy');
        // Find some documents
        //collection.find('a': 3).toArray(function(err, docs) 
        collection.find().toArray(function(err, docs) 
          assert.equal(err, null);
          //console.log(docs);
          //console.log('docs.length ---> ', docs.length);
          console.log('Creating CSV...');
          //console.log('-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=');
          var json2csv = require('json2csv');
          var fields = ['_id', 'JobID', 'LastApplied'];
          var fieldNames = ['ID_OR_PK', 'JOB_UNIQUE_ID_TITLE', 'APPLICATION_DATE'];
          var data = json2csv( data: docs, fields: fields, fieldNames: fieldNames );
          //console.log('-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=');
          // EXPORT FILE
          res.attachment('yourfilenamehere.csv');
          res.status(200).send(data);
          callback(docs);
        );
    ;

    // Use connect method to connect to the server
    MongoClient.connect(url, function(err, db) 
      assert.equal(null, err);
      console.log("Connected successfully to server");
      //
      // migrate db table to some format TBD
      //
      migrateMongoDBTable(db, function() 
        db.close();
      );
    );

)

app.listen(3000, function () 
  console.log('Example app listening on port 3000!')
)
// Connection URL
//var url = 'mongodb://localhost:27017/myproject';

【讨论】:

【参考方案6】:

我在http://nikgrozev.com/2017/05/10/mongo-query-to-CSV-download-expressjs/找到了解决方案

关键字

将输出流式传输到 HTTP 响应

【讨论】:

【参考方案7】:

使用 json2csv 库,您可以从 mongodb 数据中导出 csv。

const json2csv = require('json2csv').parse;

//For unique file name
const dateTime = new Date().toISOString().slice(-24).replace(/\D/g, 
'').slice(0, 14); 

const filePath = path.join(__dirname, "../../../", "public", "exports", "csv-" 
+ dateTime + ".csv");

let csv; 

const student = await req.db.collection('Student').find().toArray();

// Logging student
// [id:1,name:"John",country:"USA",id:1,name:"Ronny",country:"Germany"]

const fields = ['id','name','country'];

 try 
    csv = json2csv(student, fields);
  catch (err) 
    return res.status(500).json(err);
 

 fs.writeFile(filePath, csv, function (err) 
    if (err) 
        return res.json(err).status(500);
    
    else 
        setTimeout(function () 
            fs.unlink(filePath, function (err)  // delete file after 30 sec
            if (err) 
                console.error(err);
            
            console.log('File has been Deleted');
        );

    , 30000);
        res.download(filePath);
    
)

【讨论】:

这里是导出到 csv 的工作示例的要点链接 gist.github.com/aabiskar/e7f028e78e586921a0b7f3435b65b5f1【参考方案8】:

json2csv 包已更新,因为写出了最高投票的答案,新版本的语法略有不同:

        var  Parser  = require('json2csv')

        const fields = [
            label: 'header 1',
            value: 'field1_name'
        , 
            label: 'header 2',
            value: 'field2_name'
        ]

        const json2csv = new Parser( fields: fields )

        try 
            const csv = json2csv.parse(data)
            res.attachment('data.csv')
            res.status(200).send(csv)
         catch (error) 
            console.log('error:', error.message)
            res.status(500).send(error.message)
        

res.attachment 是一个函数,而不是一个属性。需要删除等号才能使其正常工作。

【讨论】:

重要!因为最重要的答案现在不起作用...... @svarrall 会在服务器上创建文件以防它部署在服务器上吗?如果有多个下载请求,我不希望服务器使用上述解决方案创建太多文件。【参考方案9】:

回答为时已晚,但对于 2021 年需要优化且耗时更少的解决方案的人来说

上述所有解决方案都很好,但有

    时间复杂度 O(n)

    存储复杂性或高内存使用问题有时应用程序可能会因为请求过多而崩溃

解决方案: 当用户对数据库进行 CRUD 操作时,维护一个同步的 CSV 文件 例如在 put 请求中

app.put('/product/:id', (res,req)=>
    // step 1 do update operation in db
    // step 2 do update operation in CSV file

    return res.send('OK 200')

)

所以下次用户请求 CSV 时,用户可以立即获取 CSV 文件

编码愉快:)

【讨论】:

【参考方案10】:

如果您不想使用模块,我会将其分为两个步骤:

第 1 步:将 JSON 转换为平面数组

这里重要的是 JSON 键成为数组的第一行

/**
 * Convert JSON list of objects to 2D array
 * Headers from first list item create first row of array
 * @param Array JSON list of objects
 * @return Array 2D array with first row as the object keys from the first object in list
 */
function json2array (data) 
  let headers = []
  let output = new Array(data.length + 1)
  // get object keys for first item if one exists
  if (data.length > 0) headers = Object.keys(data[0])
  output[0] = headers
  data.forEach((dataRow, row) => 
    const outputRow = new Array(headers.length)
    // populate array
    headers.forEach((header, column) => 
      outputRow[column] = dataRow[header]
    )
    output[row + 1] = outputRow
  )
  return output


// jsonData = [email: "user1@example.com", name: "John", email: "user2@example.com", name: "Jane"] 

const arr = json2array(jsonData)

// arr = [["email", "name"],["user1@example.comm", "John"],["user2@example.com", "Jane"]]

第 2 步:将二维数组转换为 CSV

然后你可以保存到文件或输出到浏览器等。

/**
 * Convert a 2D array into CSV notation
 * @param Array a 2D array with the first row as headers
 * @return String CSV-formatted text
 */
function array2csv (arr) 
  let csvOut = ''
  arr.forEach((arrRow, rowNum) => 
    let csvRow = ''
    arrRow.forEach((value, colNum) => 
      if (colNum > 0) csvRow += ',' // comma-separate columns
      const valueType = typeof(value)
      if (valueType == 'number') csvRow += value
      else if (valueType == 'boolean') csvRow += value ? 'TRUE':'FALSE'
      // quote strings and handle existing quotes
      else csvRow += '"' + value.replace(/"/g, '""') + '"'
    )
    if (rowNum > 0) csvOut += "\n" // newline-split rows
    csvOut += csvRow
  )
  return csvOut


const csvOut = array2csv(arr)

/*
"email","name"
"user1@example.com","John"
"user2@example.com","Jane"
*/

这些方法是O(n) 复杂度。

【讨论】:

【参考方案11】:

这里是如何生成一个csv文件的过程

安装

npm i fs-extra

像这样创建一个文件

const fse = require('fs-extra');
const fs = require('fs');
const csvInstant_save = exports.csvInstant_save = (data=[], filePath='') => 

    let csvContent = "";

    let header = Object.keys(data[0]);
    csvContent += header + "\r\n";
    
    data.forEach(function(rowArray) 
        let row_data = Object.values(rowArray)
        let row = row_data.join(",");
        csvContent += row + "\r\n";
    );
    
    fse.ensureFileSync(filePath)

    const writeStream = fs.createWriteStream(filePath, encoding: 'utf8');
    writeStream.write(csvContent);

这样调用函数

const exceljshelper = require('../helpers/exceljshelper');

exceljshelper.excelInstant_save(data, './app/public/uploads/exports/user/excel/users-data-'+todayYMD+'.csv')

【讨论】:

【参考方案12】:

真实的例子可能有帮助

BACKEND - NodeJs

路由器.js

var  Parser  = require('json2csv');
const dataService = require('../service');

router.get('/exportData', cors(), async (req, res) => 
    const list = await dataService.getData(req);
    const fields = [
         label: 'Id', value: 'Id' ,
         label: 'Name', value: 'Name' ,
         label: 'Age', value: 'Age' ,
         label: 'Date', value: 'Date' ,
    ];
    const json2csv = new Parser( fields: fields );
    const csv = json2csv.parse(list);
    res.status(200).send(Buffer.from(csv));
);

service.js

const sequelize = require('sequelize');
const moment = require('moment');
const  CallLogs  = require("../models");
const  Op  = sequelize;

exports.getData = async (req) => 
    let list = [];
    try 
        const  query:  Date_gte, Date_lt   = req;
        const request = 
            where: ,
            order: [['Date', 'DESC']],
        ;
        if (Date_gte && Date_lt) 
            request.where.Date = 
                [Op.between]: [Date_gte, Date_lt]
            
         else 
            if (Date_gte) 
                request.where.Date = 
                    [Op.gte]: Date_gte
                
            
            if (Date_lt) 
                request.where.Date = 
                    [Op.lt]: Date_lt
                
            
        

        list = JSON.parse(JSON.stringify(await CallLogs.findAll(request)));

        list = list.map(c => 
            let obj =  ...c ;
            obj.Date = moment(new Date(obj.Date)).format('DD.MM.YYYY');
            return obj;
        );
     catch (error) 
        console.log(error);
    
    return list;

前端 - 反应

app.js

import download from "downloadjs";
import Axios from "axios";

async function exportResultToExcel() 
    try 
        let filters =  ...this.state.filter ;
        const res = (await Axios.get(`api/exportData`, 
            params: filters,
            responseType: "blob"
        )).data;
        if (res) 
            download(res, new Date().toLocaleDateString() + '-data.csv');
         else 
            alert("Data not found");
        
     catch (error) 
        console.log(error);
    


<Button onClick=() =>  this.exportResultToExcel() >Export to CSV</Button>

【讨论】:

以上是关于如何导出csv nodejs的主要内容,如果未能解决你的问题,请参考以下文章

java导出csv如何避免内存溢出

如何使用 angular 和 nodejs 将 csv 数据转储到 mongoDB

如何使用PHP导出csv和excel文件

java如何导出csv文件 用户点击导出可以导出到他想保存的地方

如何将 Firebase 实时数据库导出为 CSV?

PHP如何导出导入CSV文件?