Sailing.js Skiper cancels file upload
I am writing a Sails-js based application where a user can upload an image file to a server. I would like to check if the file is actually an image file before saving it to disk.
After struggling with the skipper adapter, I noticed that apparently I was unable to check the properties of the file until the download was complete. If I checked the file type and tried to respond res.json("error, wrong file type")
or something like that, the client never got a response unless I let the download finish and only respond after that . This also means that all checks must be performed after downloading . This is especially frustrating since I have a Sails policy that enforces an endpoint through a. The unfinished download is somehow blocking even the authentication middleware that needs to run in front of the controller.
Below is my best attempt, which just saves the file to disk and deletes it if validation fails. Very unsatisfactory. Also, it will never respond if an unauthenticated user tries to download a file:
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.'});
}
});
}
After getting frustrated with Skipper's Disk Adapter, I looked through its documentation and found docs on writing my own receiver . Armed with this information and this StackOverflow question , I created the following code:
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 });
});
This way I have a little more control over the streams and I think I got the basic idea of ββgetting a stream from a request and then connecting it to the file stream. In a readable event, I am trying to check the file type. However, thread interruption is still a problem. After some trial and error, I was able to call functions end()
for both file streams and output. If I understood threads correctly, they should close them. I am getting the correct return expression from my function req.file('image).upload
right after the receiver detects the wrong file type. However, I can still get the answer back! After trying with a really large file, it looks like
file.on('data', function(d) {
console.log(d)
});
keeps on registering new chunks, which means the file stream was not closing as I expected.
So, at the end, my final question is , how can I properly cancel the incoming HTTP request stream in Sails.js / Skipper?
source to share
No one has answered this question yet
See similar questions:
or similar: