使用节点js的S3文件上传流

Posted

技术标签:

【中文标题】使用节点js的S3文件上传流【英文标题】:S3 file upload stream using node js 【发布时间】:2014-03-06 15:11:18 【问题描述】:

我正在尝试使用具有要求的节点 js 服务器在亚马逊 S3 上流式传输文件的解决方案:

不要将临时文件存储在服务器或内存中。但是在某些限制下不完整的文件,可以使用缓冲来上传。 对上传的文件大小没有限制。 在文件上传完成之前不要冻结服务器,因为在上传大量文件的情况下,其他请求的等待时间会意外地出现 增加。

我不想使用从浏览器直接上传文件,因为在这种情况下需要共享 S3 凭据。从 node js 服务器上传文件的另一个原因是上传文件之前可能还需要应用一些身份验证。

我尝试使用 node-multiparty 来实现这一点。但它没有按预期工作。您可以在https://github.com/andrewrk/node-multiparty/issues/49 看到我的解决方案和问题。它适用于小文件,但适用于大小为 15MB 的文件。

任何解决方案或替代方案?

【问题讨论】:

【参考方案1】:

您现在可以在“将文件上传到 Amazon S3 存储桶”部分中通过 official Amazon SDK for nodejs 使用流式传输,或查看他们的 example on GitHub。

更棒的是,您终于可以做到事先不知道文件大小。只需将流作为Body 传递:

var fs = require('fs');
var zlib = require('zlib');

var body = fs.createReadStream('bigfile').pipe(zlib.createGzip());
var s3obj = new AWS.S3(params: Bucket: 'myBucket', Key: 'myKey');
s3obj.upload(Body: body)
  .on('httpUploadProgress', function(evt)  console.log(evt); )
  .send(function(err, data)  console.log(err, data) );

【讨论】:

这不适用于来自yazl zip 对象的输出流? 太棒了!您还可以通过将pipe 转换为流来缓冲到zlib.createGzip()const Duplex = require('stream');` 有谁知道这是怎么工作的?如果每个部分都是固定大小的,如果最后一个部分与完整大小不完全匹配,他们如何填写? 你能更新链接 Johann 吗?它似乎已经改变了。 @anon58192932 感谢您的关注,链接现已更新!【参考方案2】:

供您参考,v3 SDK 发布时带有一个专用模块来处理该用例:https://www.npmjs.com/package/@aws-sdk/lib-storage

我花了一段时间才找到它。

【讨论】:

遇到了这个问题,传入的流被转换为 geojson 特征集合。【参考方案3】:

试试https://www.npmjs.org/package/streaming-s3。

我用它来并行上传几个大文件(>500Mb),效果很好。 它非常可配置,还允许您跟踪上传统计信息。 您不需要知道对象的总大小,也不会在磁盘上写入任何内容。

【讨论】:

【参考方案4】:

我在工作项目here 中使用s3-upload-stream 模块。

@raynos 在他的http-framework 存储库中也有一些很好的例子。

【讨论】:

【参考方案5】:

您也可以查看 - https://github.com/minio/minio-js。它具有最少的抽象 API 集来实现最常用的 S3 调用。

以下是流式上传的示例。

$ npm install minio
$ cat >> put-object.js << EOF

var Minio = require('minio')
var fs = require('fs')

// find out your s3 end point here:
// http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region

var s3Client = new Minio(
  url: 'https://<your-s3-endpoint>',
  accessKey: 'YOUR-ACCESSKEYID',
  secretKey: 'YOUR-SECRETACCESSKEY'
)

var outFile = fs.createWriteStream('your_localfile.zip');
var fileStat = Fs.stat(file, function(e, stat) 
  if (e) 
    return console.log(e)
  
  s3Client.putObject('mybucket', 'hello/remote_file.zip', 'application/octet-stream', stat.size, fileStream, function(e) 
    return console.log(e) // should be null
  )
)
EOF

putObject() 这里是一个完全托管的单个函数调用,文件大小超过 5MB,它会在内部自动执行多部分。您也可以恢复失败的上传,它将通过验证之前上传的部分从上次中断的地方开始。

另外这个库也是同构的,也可以在浏览器中使用。

【讨论】:

这个库流可以从上传用户上传文件,而不是我必须先将它缓冲到我的服务器(无论是在内存还是磁盘上)? 它接受输入流,它可以是文件流或任何流。它会自动上传到服务器,直到流关闭。【参考方案6】:

如果它对我能够成功从客户端流式传输到 s3 的任何人有所帮助(无需内存或磁盘存储):

https://gist.github.com/mattlockyer/532291b6194f6d9ca40cb82564db9d2a

服务器端点假定req 是一个流对象,我从客户端发送了一个文件对象,现代浏览器可以将其作为二进制数据发送,并在标头中添加了文件信息集。

const fileUploadStream = (req, res) => 
  //get "body" args from header
  const  id, fn  = JSON.parse(req.get('body'));
  const Key = id + '/' + fn; //upload to s3 folder "id" with filename === fn
  const params = 
    Key,
    Bucket: bucketName, //set somewhere
    Body: req, //req is a stream
  ;
  s3.upload(params, (err, data) => 
    if (err) 
      res.send('Error Uploading Data: ' + JSON.stringify(err) + '\n' + JSON.stringify(err.stack));
     else 
      res.send(Key);
    
  );
;

是的,将文件信息放在标题中会破坏约定,但如果你看一下要点,它比我使用流式库或 multer、busboy 等发现的任何其他东西都要干净...

+1 表示实用主义,感谢@SalehenRahman 的帮助。

【讨论】:

以上是关于使用节点js的S3文件上传流的主要内容,如果未能解决你的问题,请参考以下文章

节点 JS AWS S3 文件上传。如何获取公共 URL 响应

从 s3 存储桶获取 2 个文件,并在使用 lambda 节点 js 将其上传到 s3 存储桶后制作 1 个 zip 文件

您可以使用流而不是本地文件上传到 S3 吗?

使用 go 将流文件上传到 AWS S3

Javascript AWS SDK S3上传方法与正文流生成空文件

将 CSV 文件从 JSON 数据上传到 S3 存储桶