【问题标题】:How to synchronously load a csv file into memory before handling HTTP requests如何在处理 HTTP 请求之前将 csv 文件同步加载到内存中
【发布时间】:2019-08-18 19:42:15
【问题描述】:

我正在尝试编写一个简单的 express/node.js 应用程序,该应用程序使用 csv 文件中的数据响应 GET 请求。我想读取这个 csv 文件来生成一个 javascript 对象(本质上是一个键值映射),然后使生成的映射可用于控制器中的 HTTP 请求处理逻辑。

我编写了一个读取 csv 文件并导出所需对象的模块,但我不确定如何确保:

  1. 此操作完成,对象在处理 HTTP 请求之前实际存在
  2. 文件操作仅在服务器启动时执行一次,而不是每个请求执行一次,从而导致大量开销

如何组织我的代码以在 express 应用的上下文中实现这些目标?

这就是我处理 CSV 文件的方式:

var myMap = {};

fs.createReadStream('filename.csv')  
  .pipe(csv())
  .on('data', (row) => {
    // Build javascript object
    myMap[row['key']] = row['value'];
  })
  .on('end', () => {
    console.log('Done.');
  });

// Does this work?
module.exports =  myMap;

【问题讨论】:

    标签: javascript node.js csv express asynchronous


    【解决方案1】:

    在文件加载到内存后确保http对象监听如何:

    
    // server.js
    var myMap = {};
    
    function readCsv(cb){
      fs.createReadStream('filename.csv')  
      .pipe(csv())
      .on('data', (row) => {
        // Build javascript object
        myMap[row['key']] = row['value'];
      })
      .on('end', () => {
        console.log('Done.');
        cb();
      });
    }
    
    var app = express();
    
    exports = Object.freeze({
      server: http.createServer(app)
      init(){
        readCsv(() => {
          this.server.listen(80)
        })
      }
    })
    
    

    类似的东西。

    你也可以使用 Promise

    // server.js
    var myMap = {};
    
    function readCsv(){
      return new Promise((resolve, reject) => {
        fs.createReadStream('filename.csv')  
        .pipe(csv())
        .on('data', (row) => {
          // Build javascript object
          myMap[row['key']] = row['value'];
        })
        .on('end', () => {
          console.log('Done.');
          resolve();
        })
        .on('error', reject)
      })
    
    }
    
    var app = express();
    
    exports = Object.freeze({
      server: http.createServer(app)
      init(){
        return readCsv().then(() => {
          this.server.listen(80)
        })
      }
    })
    
    

    【讨论】:

    • 我终于明白了如何使用自定义 Promise 来处理异步代码。谢谢!
    【解决方案2】:

    我会寻找更同步的方式来读取文件和处理 http 请求。这是它应该是什么样子的示例代码,

    import fs from 'fs';
    
    async function processCSV() {
        try {
            let map = await readCsv();
            //handle http request in another function with same async await way
            let http = await processHttpRequest(map);
    
            // process  the http response
        } catch (e) {
            console.log('e', e);
        }
    
    }
    
    function readCsv()
    {
        let myMap = [];
        fs.createReadStream('filename.csv')
            .pipe(csv())
            .on('data', (row) => {
                // Build javascript object
               return myMap[row['key']] = row['value'];
            })
            .on('end', () => {
                console.log('Done.');
            });
    }
    async function processHttpRequest(map)
    {
        try
        {
            let reqres = await httpReuqest(map); // Your defined function for httpReuqest
        }
        catch (e)
        {
    
        }
    
    }
    processCSV();
    processHttpReuqet();
    

    【讨论】:

      【解决方案3】:

      为了实现这两个目标,您可以将代码包含在 app.js 文件中。 App.js 仅在快速服务器启动时运行。它不会在页面刷新时重新加载。您可以在 readstream 结束后运行 app.listen。

      var myMap = {};
      
      fs.createReadStream('filename.csv')  
        .pipe(csv())
        .on('data', (row) => {
          // Build javascript object
          myMap[row['key']] = row['value'];
        })
        .on('end', () => {
          app.listen(port, () => console.log(`Example app listening on port ${port}!`));
        });
      

      但是,由于我认为您不会拥有大量数据,因此对于 csv 解析器和文件读取器,最好使用同步(阻塞)方法。这只会让它更容易理解。我在下面使用csv-parse。

      const express = require('express')
      const fs = require('fs')
      const parse = require('csv-parse/lib/sync')
      
      const app = express()
      const port = 3000
      
      /* In this example assume myMap will be
      / `
      / "key_1","key_2"
      / "value 1","value 2"
      / `
      */
      var myMap = fs.readFileSync('sample.csv', 'utf8');
      
      /* parsing the csv will return:
      /  [Object {key_1: "value 1", key_2: "value 2"}]
      */
      const records = parse(myMap, {
        columns: true,
        skip_empty_lines: true
      })
      
      app.get('/', (req, res) => res.send('Hello World!' + records[0].key_1))
      app.listen(port, () => console.log(`Example app listening on port ${port}!`))
      

      test it on runkit

      【讨论】:

        【解决方案4】:

        更新:

        使用https://csv.js.org/parse/

        以下一个已弃用,不再维护。

        已弃用:

        您好,我创建了一个 npm 包来同步读取 CSV 或作为承诺:

        https://www.npmjs.com/package/csv-parser-sync-plus-promise

        说明:

        csv-parser-sync-plus-promise

        一个同步读取 csv 或作为 promise 的模块

        特点

        现在同步读取任何 csv 或作为 promise。选择是你的

        用法

        let parser = require('csv-parser-sync-plus-promise')
        

        // 用于同步

        let a=parser.readCsvSync('<filepath>')
        

        // 承诺

        let b=parser.readCsvPromise('<filepath>')
        
        **Note:** You can use both fully qualified and relative paths <filepath>
        

        错误

        所有错误都将打印为 console.error 并且进程将退出并退出代码 222

        【讨论】:

        • 我尝试将您的库作为 csv-parser 的直接替代品来解决这种确切类型的问题。不幸的是,在 csv-parser 在 1 秒内异步处理 1000 行文件的地方,您的库似乎挂了很长时间。不知道怎么了。不过我喜欢这个想法,尤其是在尝试使用从 CSV 加载的异步样本数据来设置异步 Jest 测试时。
        • @JasonD hi 使用 csv-parse
        • @JasonD 我犯了一个更好的模块csv.js.org/parse 所以我的不再维护了
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-08
        • 1970-01-01
        • 1970-01-01
        • 2021-06-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多