我正在编写基于Sails-js的应用程序,用户可以将图像文件上传到服务器。在将文件保存到磁盘之前,我想检查文件是否真的是图像文件。
在与船长磁盘适配器挣扎之后,我注意到在完成上传之前显然我无法检查文件属性。如果我检查了文件类型并尝试使用res.json("error, wrong file type")
或类似内容进行回复,则客户端永远不会得到响应,除非我让上传完成并仅在此之后做出响应。这也意味着我必须在上传后执行所有验证。由于我有通过中间件保护端点的Sails策略,因此特别讨厌。未完成的上传以某种方式设法阻止应该在控制器之前运行的认证中间件。
以下是我最好的尝试,只是将文件保存在磁盘上,如果验证错误,请将其删除。非常不满意。此外,如果未经身份验证的用户尝试上传文件,它将永远不会响应:
create: function (req, res) {
var valid = true;
if (!req.body.headline || !req.body.content) {
valid = false;
}
var settings = {
maxBytes: 10000000,
dirname: path.join(sails.config.appPath, '/assets/images')
};
req.file('image').upload(settings, function whenDone(err, uploadedFiles) {
if (err) {
return res.negotiate(err);
}
if (uploadedFiles[0]) {
var upload = req.file('image')._files[0].stream,
headers = upload.headers,
allowedTypes = ['image/jpeg', 'image/png'];
if (_.indexOf(allowedTypes, headers['content-type']) === -1 || !valid) {
//file type is not supported, abort and delete file
/** TODO: Ideally we would check the file type and other validations before upload and saving. However,
* skipper didn't seem to support any obvious ways to abort requests at the moment of writing:
* https://github.com/balderdashy/skipper/issues/80
* https://stackoverflow.com/questions/31061719/sails-js-with-skipper-check-if-file-input-were-empty-before-starting-upload
*
* Therefore returning from this request before finishing upload results hanging response. Investigate
* alternatives when more time.
*
* NOTE: If unauthenticated user with file upload tries to use this endpoint, they will not get response, since
* authentication middleware rejects request but skipper prevents server from responding!
*/
var fileAdapter = SkipperDisk();
return fileAdapter.rm(uploadedFiles[0].fd, function () {
res.status(400);
return res.json({message: 'Wrong fileformat or missing attributes. Please provide .png or .jpg file and ' +
'ensure you have content and headline fields defined.'});
});
}
}
if (valid) {
Announcement.create({
headline: req.body.headline,
content: req.body.content,
author: req.session.user})
.then(function(announcement) {
if (uploadedFiles[0]) {
announcement.imagePath = uploadedFiles[0].fd;
announcement.imageUrl = '/announcements/' + announcement.id + '/image';
} else {
announcement.imagePath = null;
announcement.imageUrl = null;
}
announcement.save(function(err, saved) {
return res.json(saved);
});
});
} else {
res.status(400);
return res.json({message: 'Missing attributes content and/or headline.'});
}
});
}
在对Skipper的磁盘适配器感到沮丧之后,我浏览了它的文档和found docs about writing my own receiver。有了这些信息,similar issue in Stackoverflow我创建了以下代码:
create: function (req, res) {
var allowedTypes = ['image/jpeg', 'image/png'];
//output stream to disk
var output = require('fs').createWriteStream('./storedImage.png');
//custom receiver for Skipper
var receiver = new stream.Writable({objectMode: true});
receiver._write = function(file, enc, done) {
file.pipe(output);
output.once('finish', function () {
console.log("file transfer finished");
receiver.end();
return done();
});
file.once('readable', function() {
//tiedoston luku aloitetaan.
var headers = req.file('image')._files[0].stream.headers;
console.log("reading file...");
req.validate({
headline: 'string',
content: 'string'
});
if (_.indexOf(allowedTypes, headers['content-type']) === -1) {
console.log("forbidden img type!");
file.end();
}
});
file.once('data', function(d) {
console.log(d)
});
file.once('end', function() {
console.log("input end");
output.end();
});
output.on('error', function (err) {
console.log("error in output stream ", err);
return done({
incoming: file,
outgoing: output,
code: 'E_WRITE',
stack: typeof err === 'object' ? err.stack : new Error(err),
name: typeof err === 'object' ? err.name : err,
message: typeof err === 'object' ? err.message : err
});
});
};
req.file('image').upload(receiver, function(err, files) {
if (err) {
return res.json("There was a problem :(");
}
console.log(files);
return res.json({success: files });
});
这样我对流有了更多的控制权,我想我想出了从请求中获取流,然后将其传递给文件流的基本思路。在可读事件中,我尝试检查文件的类型。但是,中止流仍然是一个问题。经过一些试验和错误后,我设法为文件和输出流调用end()
函数。如果我正确理解了流,他们应该关闭它们。在接收器检测到错误的文件类型后,我立即从req.file('image).upload
函数中获得正确的返回值。 但是,我仍然可以返回响应!尝试使用非常大的文件后,它看起来像
file.on('data', function(d) {
console.log(d)
});
不断记录新的块,这意味着文件流没有像我预期的那样关闭。
所以最后我的最后一个问题是,如何在Sails.js / Skipper中正确中止传入的http请求流?