2013-06-26 4 views
11

Я пытаюсь передать файл, отправленный через форму непосредственно в ведро Amazon S3, используя aws-sdk или knox. Обработка формы осуществляется с помощью formidable.Поток загрузки файла на S3 на Node.js с использованием грозного и (knox или aws-sdk)

Мой вопрос: как правильно использовать грозное с aws-sdk (или knox), используя новейшие функции каждой из этих библиотек для обработки потоков?

Я понимаю, что эта тема уже здесь просили в различных вкусов, а именно:

Однако, я считаю, что ответы немного устарели и/или вне темы (т. Е. Поддержка CORS, которую я сейчас не хочу использовать для fo r различные причины) и/или, самое главное, не ссылаются на последние функции от aws-sdk (см.: https://github.com/aws/aws-sdk-js/issues/13#issuecomment-16085442) или knox (особенно putStream() или его readableStream.pipe (req) вариант, both explained in the doc).

После долгих боев я пришел к выводу, что мне нужна помощь (отказ от ответственности: я довольно новичок с потоками).

HTML форма:

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

Экспресс 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: Your socket connection to the server was not read from or written to within the timeout period. Idle connections will be closed.]

message: 'Your socket connection to the server was not read from or written to within the timeout period. Idle connections will be closed.', code: 'RequestTimeout', name: 'RequestTimeout', statusCode: 400, retryable: false }

Нокс версия handlePart() функция с учетом так и с треском терпит неудачу:

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 где-то.

Регион настроен на eu-west-1 в обоих случаях.

Дополнительные примечания:

node 0.10.12

latest formidable from npm (1.0.14)

latest aws-sdk from npm (1.3.1)

latest knox from npm (0.8.3)

ответ

10

Ну, according to the creator of Formidable, прямой потоковый на Amazon S3 невозможно:

The S3 API requires you to provide the size of new files when creating them. This information is not available for multipart/form-data files until they have been fully received. This means streaming is impossible.

Действительно, form.bytesExpected относится к размеру всей форме, и а не размер одного файла.

Данные должны поэтому либо попадать в память или диск на сервер, прежде чем загружаться на S3.

+3

Это просто спас меня много времени. Благодарю. –

+6

На самом деле это не так. Можно передать/передать поток s3! вам просто нужно знать размер загрузки. Если ваш клиент может обеспечить, что тогда вы действительно можете использовать канал для загрузки на s3 без неприятного записи на жесткий диск. Я пишу cli и посреднический сервер, который будет загружаться на s3. Поскольку я контролирую как клиент, так и сервер, я могу определить размер файла перед загрузкой. Я думаю, что могут быть другие краевые дела, такие как мои, которые не должны быть уволены. Я использую knox для потоковой передачи в s3 с запросом put. – CharlesTWall3

+0

@ CharlesTWall3 Это очень полезный комментарий, я не думал об этом в то время - я думал о решении только на стороне сервера. Не стесняйтесь публиковать ответ, если вам удастся что-то сделать, я с радостью проголосую за ваше решение. Вы также можете отредактировать этот ответ. Благодаря! – jbmusso

2

Использование multipartUpload AWS S3 (в s3-upload-stream в качестве рабочего модуля) и узел-грозным-х readable stream, вы можете направить поток, чтобы загрузить как this:

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); 
+0

Я использовал это с многопартийностью – ifiok

2

Поскольку этот пост настолько стар, и я считаю, потоковое прямо сейчас поддерживается, я потратил много времени на чтение из финиковых ответов на эту тему ...

Если это поможет кому я был в состоянии потока от клиента к s3 напрямую, без необходимости установки пакетов:

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

Сервер принимает req является объектом потока, в моем случае объект файла был использован в XHR (отправить), которые будут передавать двоичные данные в современных браузерах.

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 за помощь.

+0

В моем случае загрузка работала, но файл изображения не мог быть открыт правильно. – IroNEDR

+0

Вы проверили свои расширения? Открыто правильно на AWS или из другой загрузки? Убедитесь, что вы тоже получаете правильное количество байтов. – mattdlockyer