2

Update:Streaming файл S3 «Error: поток закончился неожиданно»

Я считаю, что это может быть вызван тем, что я использую анализатор тела при условии курьерского. Может ли это быть возиться с потоком, который многопартий пытается разобрать?

Я основываю свое решение на this answer.

Что я пытаюсь сделать: Поток файла из клиентского браузера прямо на S3 с моим сервером NodeJS, выступающим в качестве прокси-сервера (для целей безопасности). Я не хочу, чтобы файл касался файловой системы сервера, чтобы избежать этого узкого места.

Я получаю следующее сообщение об ошибке:

events.js:72 
     throw er; // Unhandled 'error' event 
      ^
Error: stream ended unexpectedly 
    at Form.<anonymous> (/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) 

Я посмотрел на код и не вполне может показаться, чтобы понять, что вызывает проблему. Я использую angular-file-upload, потому что я использую угловой на переднем конце. Вот то, что запрос выглядит следующим образом:

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-- 

А вот то, что мой код выглядит следующим образом:

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

Похоже, та часть, которая взрывает является multiparty, и я заглянул в этот код немного бит безрезультатно. Я не уверен, что неправильно делаю запрос или что-то не так с кодом сервера. Я не думаю, что это имеет какое-то отношение к моему ведерке S3, но я полагаю, что это может быть и так.

В любом случае, любые советы приветствуются.

+0

Зачем использовать трубку в стойке вместо отправки непосредственно на S3? –

+0

@CharlieBrown, который пришел из примера, который я использую. Я пробовал прочитать этот код, и я не понимаю его. У вас есть предложение по лучшему способу сделать это? – kentcdodds

+0

В приведенном выше примере этот код не существует. Если вы подключите деталь к счетчику, ее можно просто перейти к счетчику, а не к конечной точке S3. Просто удалите код счетчика и передайте part.byteCount непосредственно в putStream. –

ответ

2

Суть в раствор, чтобы не использовать (по-видимому, не рекомендуется) bodyParser(). Я не уверен, что он делает, но он закручивает части формы, которые использует multiparty. Поэтому вместо того, если у вас есть такая же проблема у меня был, вместо того, чтобы использовать bodyParser(), использовать вещи, которые нужны явно (например):

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

, а затем для многочастных вещей, просто используйте многопартийный разобрать организм самостоятельно , Автор многопартийности дает more info по этому вопросу.

+0

............... – arcseldon

2

Существует пример для S3 в источнике с многостраничным узлом.

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(
      '<form action="/upload" enctype="multipart/form-data" method="post">' + 
      '<input type="text" name="path"><br>' + 
      '<input type="file" name="upload"><br>' + 
      '<input type="submit" value="Upload">' + 
      '</form>' 
     ); 
    } 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 + '/'); 
}); 

`` ``

+0

Я смог воспроизвести это успешно. Огромное спасибо. Похоже, моя проблема в том, что я отправляю файл.Вот разница в запросах: http://www.diffchecker.com/jdsi7yx8 – kentcdodds

Смежные вопросы