我正在编写一个基于 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 的磁盘适配器感到沮丧后,我浏览了它的文档并找到了关于编写我自己的接收器的文档。借助 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-request 流?