multiparty icon indicating copy to clipboard operation
multiparty copied to clipboard

Feature suggestion: ability to cancel the upload process

Open endymion00 opened this issue 11 years ago • 11 comments

Provide an event (i.e: 'cancel') in order that when emitted the upload process were canceled.

endymion00 avatar Oct 25 '13 22:10 endymion00

I'm not sure what you mean about this being an event, but I agree we need to be able to abort the upload process.

andrewrk avatar Dec 17 '13 21:12 andrewrk

I meant that maybe some logic could decide the file upload should be canceled for some reason. For example: a logic that monitorizes file size and if it is greater than "x" then needs to cancel the upload. Let's suppose that this logic is placed inside a event listener where has access to data chunks or file info in general and it is required to be able to stop the upload. I suppose this would be done by emitting an event that when emitted the library could stop the upload process. It is just an idea :)

endymion00 avatar Dec 17 '13 22:12 endymion00

I see this working a few ways:

  1. Check the return value of the 'field' and 'part' event handlers to determine whether or not to cancel parsing.
  2. Provide a method on the Form object or 'field' and 'part' event handler arguments that does the same.

Right now, I'm having to use closure and ReadableStream's resume method to continue the parsing and handle the state properly in the 'close' and 'error' events.

nmschulte-aviture avatar Jan 04 '14 23:01 nmschulte-aviture

I implemented something similar in my application-logic, piping to black-hole-stream in order to cancel the upload. This seems like the only way to discard a readable stream. Correct me if i'm wrong :)

 form
        .on('part', function (fileStream) {
            //discard if wrong imageType
            if (allowedImageTypes.indexOf(req.params.imageType) === -1) {
                fileStream.pipe(new BlackHoleStream());
                return;
            }

           //save if everything worked fine
           saveFile(...)
   });

It might make extend part with a cancel method, which pipes part.pipe(New BlockHoleStream());. This method could also be called if the part event handlers returns an error or false.

meaku avatar Jan 18 '14 10:01 meaku

I'm interested in this as well. Currently doing the /dev/null approach:

form.on('part', function (part) {
  if (!part.filename || /\.(gif|jpe?g|png)$/i.test(part.filename)) {
    return part.pipe(fs.createWriteStream('/dev/null'));
  }
});

Starefossen avatar Jun 11 '14 11:06 Starefossen

@Starefossen your example shows that you want to be able to ignore certain parts. That seems like a desirable feature to me. We could also add that to the file hook so people could (if they acted sync on the event) prevent the temporary file from even being written.

dougwilson avatar Jun 11 '14 13:06 dougwilson

I think, a cancel method is very fundamental for this library. Is there any news on this? Thanks.

onury avatar Dec 14 '14 17:12 onury

This would be a great a useful feature! +1 :+1:

jamesmorgan avatar Feb 12 '15 12:02 jamesmorgan

@Starefossen @meaku I have been trying to cancel the streams using both ways which you suggested and this seems to not work as I expect? What I am finding is that the stream seems to timeout before any response is sent to the client. I have tried to many ways like form.emit('error), form.handlerError() and various pause, cancel methods etc. Do you know of a way to fail fast the upload process if something?

jamesmorgan avatar Feb 12 '15 17:02 jamesmorgan

To clarify, none of the examples shown here actually cancel parsing of the request, they just toss the bits to /dev/null or such. As I described, if you actually want to bail due to some logic around a part of the form, you have to track that fact via some variable (via e.g. closure), and then in your 'close' (and 'error'!) handlers, you should check that variable first, so you can emit the proper response (that there was an error with a part, rather than a subsequent request stream error or the finishing of parsing of the form).

Further, I found that simply calling resume() on the part (the first argument of the 'part' event handler) has the same effect as piping the part to a black hole. Is that not the case? Why the suggestion of a black hole pipe?

Regardless, the black hole piping and my resume() suggestion still don't resolve the issue of being able to bail out of parsing (and respond accordingly; with an unambiguous [error] message!) the form due to an error. Rather, they just allow you to work around the issue (of not being able to cancel), by continuing form parsing while ignoring the current part's (and future parts') data. You still have to finish parsing the form and handle response generation accordingly.

What is being suggested here is a way to stop parsing of the rest of the form due to an logic error during earlier parsing of the form. There is still no supported way to do this, and the user must jump through hoops to handle these cases. I still stand by my original suggestions for supporting this.

nmschulte-aviture avatar Feb 12 '15 17:02 nmschulte-aviture

Here is a solution that works for me. VERY IMPORTANT: you MUST set the Connection header to close!

form.on('part', function(part) {
  if (![some condition I want to be met]) {
    form.emit('error', new Error('File upload canceled by the server.'));
    return;
  }
  ... code to handle the uploading process normally ...
});

form.on('error', function(err) {
  console.log('ERROR uploading the file:',err.message);
  res.header('Connection', 'close');
  res.status(400).send({ status:'error' });
  setTimeout(function() { res.end(); }, 500);
  next(err);
});

AleCaste avatar Jul 18 '15 16:07 AleCaste