JSON到csv再到gzip再到s3,同时在不使用太多内存的情况下进行流式传输

JSON到csv再到gzip再到s3,同时在不使用太多内存的情况下进行流式传输,json,node.js,csv,amazon-s3,stream,Json,Node.js,Csv,Amazon S3,Stream,我们需要在node js中实现一个遵循以下流程的cron服务: postgres批量数据查询(约500mb) 将json数据转换为另一个json 将json转换为csv gzip 使用“上载”方法上载到s3 显然,我们需要使用流来实现这个过程,而不会产生内存开销 我们遇到了很多问题: 我们正在使用sequelize,一种SQLORM。有了它,我们无法流式处理查询。因此,我们正在将查询返回的JSON转换为可读的流 我们找不到一种优雅而聪明的方法来实现转换流,以转换查询返回的json。(例如输入->

我们需要在node js中实现一个遵循以下流程的cron服务:

  • postgres批量数据查询(约500mb)
  • 将json数据转换为另一个json
  • 将json转换为csv
  • gzip
  • 使用“上载”方法上载到s3
  • 显然,我们需要使用流来实现这个过程,而不会产生内存开销

    我们遇到了很多问题:

  • 我们正在使用sequelize,一种SQLORM。有了它,我们无法流式处理查询。因此,我们正在将查询返回的JSON转换为可读的流
  • 我们找不到一种优雅而聪明的方法来实现转换流,以转换查询返回的json。(例如输入->[{a:1,b:2}..]->输出->[{a1:1,b1:2}..]
  • 记录并尝试写入fs而不是s3时(使用fs.createWriteStream),似乎文件是在管道启动的同时创建的,但其大小约为10字节,并且只有在流处理完成后才保持一致。此外,使用了大量RAM,流处理在内存使用方面似乎毫无用处
  • 您将如何在NodeJS中编写此流? 在我的实验中,我使用了以下库:

    • json2csv流
    • JSONStream
    • 双簧管
    • 兹利布
    • 财政司司长
    • aws sdk

    既然Sequelize结果正在被读取到内存中,我不认为设置一个流来转换JSON有什么意义(与直接操作内存中已经存在的数据相反),但是假设您将Sequelize查询移植到提供流的mysql,您可以使用类似的方法:

    const es       = require('event-stream');
    const csv      = require('fast-csv');
    const gzip     = require('zlib').createGzip();
    const AWS      = require('aws-sdk');
    const s3Stream = require('s3-upload-stream')(new AWS.S3());
    
    // Assume `connection` is a MySQL connection.
    let sqlStream = connection.query(...).stream();
    
    // Create the mapping/transforming stream.
    let mapStream = es.map(function(data, cb) {
      ...modify `data`...
      cb(null, data);
    });
    
    // Create the CSV outputting stream.
    let csvStream = csv.createWriteStream();
    
    // Create the S3 upload stream.
    let upload = s3Stream.upload(...);
    
    // Let the processing begin.
    sqlStream.pipe(mapStream).pipe(csvStream).pipe(gzip).pipe(upload);
    
    const JSONStream = require('JSONStream');
    
    someJSONOutputtingStream.pipe(JSONStream.parse('*'))
    
    如果“输入流”发出JSON,您可以用以下内容替换
    sqlStream

    const es       = require('event-stream');
    const csv      = require('fast-csv');
    const gzip     = require('zlib').createGzip();
    const AWS      = require('aws-sdk');
    const s3Stream = require('s3-upload-stream')(new AWS.S3());
    
    // Assume `connection` is a MySQL connection.
    let sqlStream = connection.query(...).stream();
    
    // Create the mapping/transforming stream.
    let mapStream = es.map(function(data, cb) {
      ...modify `data`...
      cb(null, data);
    });
    
    // Create the CSV outputting stream.
    let csvStream = csv.createWriteStream();
    
    // Create the S3 upload stream.
    let upload = s3Stream.upload(...);
    
    // Let the processing begin.
    sqlStream.pipe(mapStream).pipe(csvStream).pipe(gzip).pipe(upload);
    
    const JSONStream = require('JSONStream');
    
    someJSONOutputtingStream.pipe(JSONStream.parse('*'))
    

    (管道的其余部分将保持不变)

    你说如果数据已经在内存中,你就看不到设置流的意义。也许我错了,但假设这种情况:我的结果查询是json格式的,存储在内存中。然后我可以直接转换这个对象。但是如果我将转换后的json转换为CSV,然后gzip CSV(不使用流),我将使用大约3倍于第一个json使用的内存。也许我错了?无论如何,我将在两小时内测试您的代码,我会让您知道!流式传输的想法并不坏,但通过流式传输修改数据似乎毫无意义(尽管我猜这取决于数据需要修改的确切方式)。您的代码运行得非常好。无论如何,我需要通过流修改数据,因为我不能使用文件系统,使用内存会花费太多。您好,我收到错误csv。createWriteStream()未定义。@Chandansinghadhwal