Доступ к необработанному файловому потоку из загружаемого файла node
Я создаю приложение, которое принимает некоторые загрузки файлов и отправляет их прямо на S3. Я бы предпочел не иметь файл tmp на моем сервере, поэтому я использую модуль Knox и хотел бы взять необработанный поток из Formidable и отправить его через Knox на S3. Я сделал что-то подобное с помощью Knox для загрузки файла с помощью этого кода:
knox.downloads.get(widget.download).on('response',function(sres){
res.writeHead(200, {
'Content-Type':'application/zip',
'Content-Length': sres.headers['content-length'],
'Content-Disposition':'attachment; filename=' + widget.download
});
util.pump(sres, res);
}).end();
Теперь я хотел бы сделать что-то подобное в oposite направлении (загрузка файла из браузера на S3).
До сих пор я написал обработчик событий для захвата каждой части данных из файла при ее загрузке:
var form = new formidable.IncomingForm();
form.onPart = function(part){
if(!part.filename){
form.handlePart(part);
}else{
if(part.name == 'download'){
// Upload to download bucket
controller.putDownload(part);
}else{
// Upload to the image bucket
controller.putImage(part);
}
//res.send(sys.inspect(part));
}
}
form.parse(req, function(err, fields, files){
if(err){
res.json(err);
}else{
res.send(sys.inspect({fields:fields, files:files}), {'content-type':'text/plain'});
//controller.createWidget(res,fields,files);
}
});
controller.putDownload = function(part){
part.addListener('data', function(buffer){
knox.download.putStream(data,part.filename, function(err,s3res){
if(err)throwError(err);
else{
console.log(s3res);
}
});
})
knox.downloads.putStream(part, part.filename, function(err,s3res){
if(err)throwError(err);
else{
console.log(s3res);
}
});
}
Но событие данных только дает мне буфер. Так можно ли захватить сам поток и нажать его на S3?
Ответы
Ответ 1
Что вы хотите сделать, это переопределить метод Form.onPart
:
IncomingForm.prototype.onPart = function(part) {
// this method can be overwritten by the user
this.handlePart(part);
};
Исключительное поведение по умолчанию заключается в том, чтобы записать деталь в файл. Вы этого не хотите. Вы хотите обрабатывать события "part" для записи в загрузку knox. Начните с этого:
form.onPart = function(part) {
if (!part.filename) {
// let formidable handle all non-file parts
form.handlePart(part);
return;
}
Затем откройте запрос knox и самостоятельно обработайте события исходной части:
part.on('data', function(data) {
req.write(data);
});
part.on('end', function() {
req.end();
});
part.on('error', function(err) {
// handle this too
});
В качестве бонуса, если req.write(data)
возвращает false, что означает, что буфер отправки заполнен. Вы должны приостановить Грозный парсер. Когда вы получаете событие drain
из потока Knox, вы должны возобновить Formidable.
Ответ 2
Используйте multiparty. Он поддерживает такой поток, как вы хотите. Он даже имеет пример потоковой передачи непосредственно на s3: https://github.com/superjoe30/node-multiparty/blob/master/examples/s3.js
Ответ 3
В промежуточном программном обеспечении Express я использую formidable
PassThrough
вместе с PassThrough
для потоковой передачи файла на S3 (в моем случае на Minio, который совместим с S3 через Minio SDK; и я считаю, что он работает и для AWS S3 с тем же Minio SDK)
Вот пример кода.
const formidable = require('formidable')
const { PassThrough } = require('stream')
const form = new formidable.IncomingForm()
const pass = new PassThrough()
const fileMeta = {}
form.onPart = part => {
if (!part.filename) {
form.handlePart(part)
return
}
fileMeta.name = part.filename
fileMeta.type = part.mime
part.on('data', function (buffer) {
pass.write(buffer)
})
part.on('end', function () {
pass.end()
})
}
form.parse(req, err => {
if (err) {
req.minio = { error: err }
next()
} else {
handlePostStream(req, next, fileMeta, pass)
}
})
И handlePostStream
выглядит так, как handlePostStream
ниже:
const uuidv1 = require('uuid/v1')
const handlePostStream = async (req, next, fileMeta, fileStream) => {
let filename = uuidv1()
try {
const metaData = {
'content-type': fileMeta.type,
'file-name': Buffer.from(fileMeta.name).toString('base64')
}
const minioClient = /* Get Minio Client*/
await minioClient.putObject(MINIO_BUCKET, filename, fileStream, metaData)
req.minio = { post: { filename: '${filename}' } }
} catch (error) {
req.minio = { error }
}
next()
}
Вы можете найти исходный код на GitHub, а также его модульные тесты.
Ответ 4
У вас нет возможности захватить поток, потому что данные должны быть переведены Formidable. buffer
вам предоставляется содержимое файла в кусках buffer.length
: это может быть проблемой, потому что, глядя на Formidable docs, кажется, что пока файл не будет полностью загружен, он не сможет достоверно сообщить размер файла и Knox put
может понадобиться.
Никогда раньше не использовал Knox, но вам может быть повезло с чем-то вроде этого:
controller.putDownload = function(part){
var req = knox.download.put(part.filename, {
'Content-Type': 'text/plain'
});
part.addListener('data', function(buffer){
req.write(buffer);
});
req.on('response', function(res){
// error checking
});
req.end();
}
Немного не уверен в битах проверки ответа, но... посмотрите, можете ли вы наклеить это на фигуру. Кроме того, Поток потока октетов из запроса на S3 с knox на node.js также имеет возможность записи, которая может быть вам полезна.