Streaming de archivo a S3 “Error: la secuencia terminó inesperadamente”

Actualizar:

Creo que esto podría deberse al hecho de que estoy usando el analizador de cuerpo proporcionado por Express. ¿Podría esto estar jugando con la secuencia que multipartita está tratando de analizar?

Estoy basando mi solución en esta respuesta .

Lo que estoy tratando de hacer: transmitir un archivo desde un navegador de cliente directamente a S3 con mi servidor NodeJS actuando como un proxy (por razones de seguridad). No quiero que el archivo toque el sistema de archivos del servidor para evitar ese cuello de botella.

Estoy recibiendo el siguiente error:

events.js:72 throw er; // Unhandled 'error' event ^ Error: stream ended unexpectedly at Form. (/Users/kentcdodds/Developer/bucketstreams/node_modules/multiparty/index.js:619:24) at Form.EventEmitter.emit (events.js:117:20) at finishMaybe (/Users/kentcdodds/Developer/bucketstreams/node_modules/multiparty/node_modules/readable-stream/lib/_stream_writable.js:443:14) at endWritable (/Users/kentcdodds/Developer/bucketstreams/node_modules/multiparty/node_modules/readable-stream/lib/_stream_writable.js:452:3) at Form.Writable.end (/Users/kentcdodds/Developer/bucketstreams/node_modules/multiparty/node_modules/readable-stream/lib/_stream_writable.js:419:5) at onend (_stream_readable.js:457:10) at process._tickCallback (node.js:415:13) 

He mirado el código y parece que no entiendo lo que está causando el problema. Estoy usando la carga de archivo angular porque estoy usando angular en la parte frontal. Así es como se ve la solicitud:

 Request URL:http://local.bucketstreams.com:3000/upload/image Request Headers CAUTION: Provisional headers are shown. Accept:application/json, text/plain, */* Cache-Control:no-cache Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryNKuH2H9IUB7kvmea Origin:http://local.bucketstreams.com:3000 Pragma:no-cache Referer:http://local.bucketstreams.com:3000/getting-started User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36 Request Payload ------WebKitFormBoundaryNKuH2H9IUB7kvmea Content-Disposition: form-data; name="type" profile ------WebKitFormBoundaryNKuH2H9IUB7kvmea Content-Disposition: form-data; name="user" pie ------WebKitFormBoundaryNKuH2H9IUB7kvmea Content-Disposition: form-data; name="file0"; filename="Screen Shot 2014-02-18 at 10.54.06 PM.png" Content-Type: image/png ------WebKitFormBoundaryNKuH2H9IUB7kvmea-- 

Y así es como se ve mi código:

 var ErrorController = require('../controller/ErrorController'); var AuthenticationController = require('../controller/AuthenticationController'); var logger = require('winston'); var http = require('http'); var util = require('util'); var multiparty = require('multiparty'); var knox = require('knox'); var Batch = require('batch'); var s3Client = knox.createClient({ secure: false, key: process.env.S3_KEY, secret: process.env.S3_SECRET, bucket: process.env.S3_BUCKET_IMAGES }); var Writable = require('readable-stream').Writable; util.inherits(ByteCounter, Writable); function ByteCounter(options) { Writable.call(this, options); this.bytes = 0; } ByteCounter.prototype._write = function(chunk, encoding, cb) { this.bytes += chunk.length; cb(); }; var supportedTypes = { profile: true, post: true }; module.exports = function(app) { app.post('/upload/image', AuthenticationController.checkAuthenticated, function(req, res) { var type = req.body.type; var userId = req.user._id; if (!supportedTypes[type]) { return ErrorController.sendErrorJson(res, 401, 'Unsupported image upload type: ' + type); } var headers = { 'x-amz-acl': 'public-read' }; var form = new multiparty.Form(); var batch = new Batch(); batch.push(function(cb) { form.on('field', function(name, value) { if (name === 'path') { var destPath = value; if (destPath[0] !== '/') destPath = '/' + destPath; cb(null, destPath); } }); }); batch.push(function(cb) { form.on('part', function(part) { if (! part.filename) return; cb(null, part); }); }); batch.end(function(err, results) { if (err) throw err; form.removeListener('close', onEnd); var destPath = '/' + userId + results[0]; var part = results[1]; var counter = new ByteCounter(); part.pipe(counter); // need this until knox upgrades to streams2 headers['Content-Length'] = part.byteCount; s3Client.putStream(part, destPath, headers, function(err, s3Response) { if (err) throw err; res.statusCode = s3Response.statusCode; s3Response.pipe(res); console.log('https://s3.amazonaws.com/' + process.env.S3_BUCKET_IMAGES + destPath); }); part.on('end', function() { console.log('part end'); console.log('size', counter.bytes); }); }); form.on('close', function(error) { logger.error(error); return ErrorController.sendErrorJson(res, 500, 'There was a problem uploading the file.'); }); form.parse(req); }); }; 

Parece que la parte que está explotando es multiparty y he investigado ese código un poco en vano. No estoy seguro de si estoy haciendo la solicitud incorrectamente o si hay algún problema con el código de mi servidor. No creo que tenga nada que ver con mi cubo S3, pero supongo que también podría ser eso.

De todos modos, cualquier consejo es bienvenido.

Hay un ejemplo para S3 en la fuente multiparty de nodo.

https://github.com/andrewrk/node-multiparty/tree/master/examples

 var http = require('http'), util = require('util'), multiparty = require('../'), knox = require('knox'), Batch = require('batch'), PORT = process.env.PORT || 27372 var s3Client = knox.createClient({ secure: false, key: process.env.S3_KEY, secret: process.env.S3_SECRET, bucket: process.env.S3_BUCKET, }); var server = http.createServer(function(req, res) { if (req.url === '/') { res.writeHead(200, { 'content-type': 'text/html' }); res.end( '
' + '
' + '
' + '' + '
' ); } else if (req.url === '/upload') { var headers = { 'x-amz-acl': 'public-read', }; var form = new multiparty.Form(); var batch = new Batch(); batch.push(function(cb) { form.on('field', function(name, value) { if (name === 'path') { var destPath = value; if (destPath[0] !== '/') destPath = '/' + destPath; cb(null, destPath); } }); }); batch.push(function(cb) { form.on('part', function(part) { if (!part.filename) return; cb(null, part); }); }); batch.end(function(err, results) { if (err) throw err; form.removeListener('close', onEnd); var destPath = results[0], part = results[1]; headers['Content-Length'] = part.byteCount; s3Client.putStream(part, destPath, headers, function(err, s3Response) { if (err) throw err; res.statusCode = s3Response.statusCode; s3Response.pipe(res); console.log("https://s3.amazonaws.com/" + process.env.S3_BUCKET + destPath); }); }); form.on('close', onEnd); form.parse(req); } else { res.writeHead(404, { 'content-type': 'text/plain' }); res.end('404'); } function onEnd() { throw new Error("no uploaded file"); } }); server.listen(PORT, function() { console.info('listening on http://0.0.0.0:' + PORT + '/'); });

“ “

El quid de la solución es no usar el bodyParser() aparentemente en desuso bodyParser() . No estoy seguro de lo que hace, pero arruina las partes del formulario que utiliza en multiparty partes. Entonces, si tienes el mismo problema que tuve, en lugar de usar bodyParser() , usa las cosas que necesitas explícitamente (por ejemplo):

 app.use(express.urlencoded()); app.use(express.json()); 

Y luego para tus cosas multiparte, solo usa multipartidismo para analizar el cuerpo tú mismo. El autor de multipartes da más información sobre el tema.