17

aws-sdkまたはknoxを使用して、フォームから送信されたファイルを直接 Amazon S3 バケットにストリーミング アップロードしようとしています。フォーム処理はformidableで行います。

私の質問は、ストリームを処理するためにこれらの各ライブラリの最新機能を使用して、aws-sdk (または knox) で formidable を適切に使用するにはどうすればよいですか?

このトピックは、ここでさまざまな形ですでに質問されていることを認識しています。

ただし、回答は少し時代遅れであるか、トピックから外れていると思います(つまり、さまざまな理由で今のところ使用したくない CORS サポート)、および/または最も重要なことに、最新の機能に言及していませんaws-sdk ( https://github.com/aws/aws-sdk-js/issues/13#issuecomment-16085442を参照) または knox (特に putStream() またはその readableStream.pipe(req) バリアント) のいずれかについて説明します。ドキュメントで)。

何時間も格闘した後、助けが必要だという結論に達しました (免責事項: 私はストリームの初心者です)。

HTML フォーム:

<form action="/uploadPicture" method="post" enctype="multipart/form-data">
  <input name="picture" type="file" accept="image/*">
  <input type="submit">
</form>

Express bodyParser ミドルウェアは次のように構成されます。

app.use(express.bodyParser({defer: true}))

POST リクエスト ハンドラ:

uploadPicture = (req, res, next) ->
  form = new formidable.IncomingForm()
  form.parse(req)

  form.onPart = (part) ->
    if not part.filename
      # Let formidable handle all non-file parts (fields)
      form.handlePart(part)
    else
      handlePart(part, form.bytesExpected)

  handlePart = (part, fileSize) ->
    # aws-sdk version
    params =
      Bucket: "mybucket"
      Key: part.filename
      ContentLength: fileSize
      Body: part # passing stream object as body parameter

    awsS3client.putObject(params, (err, data) ->
      if err
        console.log err
      else
        console.log data
    )

ただし、次のエラーが発生します。

{ [RequestTimeout: サーバーへのソケット接続が、タイムアウト時間内に読み取られなかったか、書き込まれませんでした。アイドル状態の接続は閉じられます。]

メッセージ: 'サーバーへのソケット接続は、タイムアウト期間内に読み書きされませんでした。アイドル状態の接続は閉じられます。'、コード: 'RequestTimeout'、名前: 'RequestTimeout'、statusCode: 400、再試行可能: false }

このように調整された handlePart() 関数の knox バージョンも惨めに失敗します。

handlePart = (part, fileSize) ->
  headers =
    "Content-Length": fileSize
    "Content-Type": part.mime
  knoxS3client.putStream(part, part.filename, headers, (err, res) ->
    if err
      console.log err
    else
      console.log res
  )      

また、どこかで 400 statusCode を持つ大きな res オブジェクトを取得します。

どちらの場合も、リージョンはeu-west-1に設定されています。

その他の注意事項:

ノード 0.10.12

npm(1.0.14)からの最新の手ごわい

npm (1.3.1) からの最新の aws-sdk

npm からの最新の knox (0.8.3)

4

4 に答える 4

10

AWS S3 の multipartUpload (作業モジュールとしてs3-upload-stream ) と node-formidable の読み取り可能なストリームを使用すると、次のようにストリームをパイプしてアップロードできます

var formidable = require('formidable');
var http = require('http');
var util = require('util');
var AWS      = require('aws-sdk');
var config = require('./config');
var s3 = new AWS.S3({
    accessKeyId: config.get('S3_ACCESS_KEY'),
    secretAccessKey: config.get('S3_SECRET_KEY'),
    apiVersion: '2006-03-01'
});
var s3Stream = require('s3-upload-stream')(s3);
var bucket = 'bucket-name';
var key = 'abcdefgh';


http.createServer(function(req, res) {

    if (req.url == '/upload' && req.method.toLowerCase() == 'post') {

        var form = new formidable.IncomingForm();
        form.on('progress', function(bytesReceived, bytesExpected) {
            //console.log('onprogress', parseInt( 100 * bytesReceived / bytesExpected ), '%');
        });

        form.on('error', function(err) {
            console.log('err',err);
        });

        // This 'end' is for the client to finish uploading
        // upload.on('uploaded') is when the uploading is
        // done on AWS S3
        form.on('end', function() {
            console.log('ended!!!!', arguments);
        });

        form.on('aborted', function() {
            console.log('aborted', arguments);
        });

        form.onPart = function(part) {
            console.log('part',part);
            // part looks like this
            //    {
            //        readable: true,
            //        headers:
            //        {
            //            'content-disposition': 'form-data; name="upload"; filename="00video38.mp4"',
            //            'content-type': 'video/mp4'
            //        },
            //        name: 'upload',
            //            filename: '00video38.mp4',
            //        mime: 'video/mp4',
            //        transferEncoding: 'binary',
            //        transferBuffer: ''
            //    }

            var start = new Date().getTime();
            var upload = s3Stream.upload({
                "Bucket": bucket,
                "Key": part.filename
            });

            // Optional configuration
            //upload.maxPartSize(20971520); // 20 MB
            upload.concurrentParts(5);

            // Handle errors.
            upload.on('error', function (error) {
                console.log('errr',error);
            });
            upload.on('part', function (details) {
                console.log('part',details);
            });
            upload.on('uploaded', function (details) {
                var end = new Date().getTime();
                console.log('it took',end-start);
                console.log('uploaded',details);
            });

            // Maybe you could add compress like
            // part.pipe(compress).pipe(upload)
            part.pipe(upload);
        };

        form.parse(req, function(err, fields, files) {
            res.writeHead(200, {'content-type': 'text/plain'});
            res.write('received upload:\n\n');
            res.end(util.inspect({fields: fields, files: files}));
        });
        return;
    }

    // show a file upload form
    res.writeHead(200, {'content-type': 'text/html'});
    res.end(
        '<form action="/upload" enctype="multipart/form-data" method="post">'+
        '<input type="text" name="title"><br>'+
        '<input type="file" name="upload" multiple="multiple"><br>'+
        '<input type="submit" value="Upload">'+
        '</form>'
    );
}).listen(8080);
于 2016-06-29T07:46:22.800 に答える
10

Formidableの作成者によると、 Amazon S3 への直接ストリーミングは不可能です。

S3 API では、新しいファイルを作成するときにそのサイズを指定する必要があります。この情報は、完全に受信されるまで multipart/form-data ファイルでは利用できません。これは、ストリーミングが不可能であることを意味します。

実際、form.bytesExpectedは、単一ファイルのサイズではなく、フォーム全体のサイズを参照します。

したがって、データは S3 にアップロードされる前に、まずサーバーのメモリまたはディスクにヒットする必要があります。

于 2013-06-26T16:54:17.470 に答える
3

この投稿は非常に古く、直接ストリーミングがサポートされるようになったと思いますので、このトピックに関する古い回答を読むのに多くの時間を費やしました...

パッケージをインストールする必要なく、クライアントから直接s3にストリーミングできた人に役立つ場合:

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

サーバーはreqストリームオブジェクトであると想定しています。私の場合、最新のブラウザーでバイナリデータを送信する xhr(send) で File オブジェクトが使用されました。

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);
    }
  });
};

はい、慣習に違反していますが、要点を見ると、他のパッケージに依存していることがわかった他の何よりもはるかにクリーンです。

プラグマティズムの+1と、@SalehenRahmanの助けに感謝します。

于 2017-04-25T20:27:51.790 に答える