我正在使用SendGrid通过电子邮件接收文件。 SendGrid解析传入的电子邮件,并将多部分表单中的文件发送到我设置的端点。
我不想要本地磁盘上的文件,所以我将它们直接传输到Amazon S3。这很完美。
但在我可以流式传输到S3之前,我需要获取目标邮件地址,以便我可以找到正确的s3文件夹。这是在表单post中名为“to”的字段中发送的。不幸的是,这个字段有时会在文件到达后到达,因此在我准备好接收流之前,我需要一种等待to-field的方法。
我以为我可以将onField包装在一个promise中,并等待onFile中的to-field。但是当这个字段到达文件后,这个概念似乎会自动锁定它。
我对展台流和承诺不熟悉。如果有人能告诉我怎么做,我真的很感激。
这是非工作伪代码:
function sendGridUpload(req, res, next) {
var busboy = new Busboy({ headers: req.headers });
var awaitEmailAddress = new Promise(function(resolve, reject) {
busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) {
if(fieldname === 'to') {
resolve(val);
} else {
return;
}
});
});
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
function findInbox(emailAddress) {
console.log('Got email address: ' + emailAddress);
..find the inbox and generate an s3Key
return s3Key;
}
function saveFileStream(s3Key) {
..pipe the file directly to S3
}
awaitEmailAddress.then(findInbox)
.then(saveFileStream)
.catch(function(err) {
log.error(err)
});
});
req.pipe(busboy);
}
答案 0 :(得分:0)
我终于有了这个工作。解决方案不是很漂亮,我实际上已经切换到另一个概念(在帖子末尾描述)。
要缓冲传入的数据,直到“到”字段到达,我使用@samcday的 stream-buffers 。当我掌握了to-field时,我将可读流发布到为数据排列的管道中。
这是代码(省略了一些部分,但有必要的部分)。
var streamBuffers = require('stream-buffers');
function postInboundMail(req, res, next) {
var busboy = new Busboy({ headers: req.headers});
//Sometimes the fields arrives after the files are streamed.
//We need the "to"-field before we are ready for the files
//Therefore the onField is wrapped in a promise which gets
//resolved when the to field arrives
var awaitEmailAddress = new Promise(function(resolve, reject) {
busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) {
var emailAddress;
if(fieldname === 'to') {
try {
emailAddress = emailRegexp.exec(val)[1]
resolve(emailAddress)
} catch(err) {
return reject(err);
}
} else {
return;
}
});
});
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
var inbox;
//I'm using readableStreamBuffer to accumulate the data before
//I get the email field so I can send the stream through to S3
var readBuf = new streamBuffers.ReadableStreamBuffer();
//I have to pause readBuf immediately. Otherwise stream-buffers starts
//sending as soon as I put data in in with put().
readBuf.pause();
function getInbox(emailAddress) {
return model.inbox.findOne({email: emailAddress})
.then(function(result) {
if(!result) return Promise.reject(new Error(`Inbox not found for ${emailAddress}`))
inbox = result;
return Promise.resolve();
});
}
function saveFileStream() {
console.log('=========== starting stream to S3 ========= ' + filename)
//Have to resume readBuf since we paused it before
readBuf.resume();
//file.save will approximately do the following:
// readBuf.pipe(gzip).pipe(encrypt).pipe(S3)
return model.file.save({
inbox: inbox,
fileStream: readBuf
});
}
awaitEmailAddress.then(getInbox)
.then(saveFileStream)
.catch(function(err) {
log.error(err)
});
file.on('data', function(data) {
//Fill readBuf with data as it arrives
readBuf.put(data);
});
file.on('end', function() {
//This was the only way I found to get the S3 streaming finished.
//Destroysoon will let the pipes finish the reading bot no more writes are allowed
readBuf.destroySoon()
});
});
busboy.on('finish', function() {
res.writeHead(202, { Connection: 'close', Location: '/' });
res.end();
});
req.pipe(busboy);
}
我真的非常喜欢这个解决方案的反馈,即使我没有使用它。我觉得这可以做得更简单和优雅。
新解决方案: 我没有等待to-field,而是将流直接发送到S3。我想,在输入流和S3保存之间放入的东西越多,由于我的代码中的错误,丢失传入文件的风险就越高。 (如果我没有响应200,SendGrid最终将重新发送该文件,但这需要一些时间。)
我就是这样做的:
此解决方案还让我有机会轻松掌握不成功的上传,因为不成功上传的占位符将不完整。
//迈克尔