Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for streaming output with option #118

Merged
merged 16 commits into from
Mar 10, 2021

Conversation

gpoole
Copy link
Contributor

@gpoole gpoole commented Aug 3, 2020

This change adds support for streaming output through a new buffer option to disable using buffers, which works around buffer size limitations I ran into when the output zip file is larger than Buffer MAX_LENGTH (1-2GB currently). This change also adds the following user-friendly error message when the buffer size is exceeded during zipping:

The output zip file is too big to store in a buffer (larger than Buffer MAX_LENGTH). To output a stream instead, set the gulp-zip buffer option to 'false'.

I've added test cases for the message and to make the vinyl file contents are a stream.

I haven't added a test to see if the stream can be successfully read since decompress-unzip unfortunately doesn't support streams. Since the stream used is just the original unbuffered output of yazl I thought the other tests might already cover it?


IssueHunt Summary

Referenced issues

This pull request has been submitted to:


index.js Outdated Show resolved Hide resolved
index.js Outdated
@@ -13,6 +13,7 @@ module.exports = (filename, options) => {

options = {
compress: true,
streamOutput: false,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why make it an option? Could you not check file.isStream() and just do it if true?

Copy link
Contributor Author

@gpoole gpoole Aug 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also originally wondered that but to explain my thinking here:

  1. We could have buffers and streams coming in, so we'd only be basing that decision off the first one we see
  2. To me, wanting to stream files in doesn't imply wanting the result as a stream as well, mainly since streams are not as well supported in the gulp ecosystem and may cause problems down the line
  3. I think having this as a default-off option would be a minor change, whereas taking the setting from the input files could be breaking since it would change the output from a buffer to a stream wherever streams are coming in

I'm happy to change it if you think I'm missing the mark there though.

Copy link
Owner

@sindresorhus sindresorhus Aug 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I don't think that's possible. A Gulp pipeline is either fully streamed or not at all, since even if only a single plugin in a pipeline doesn't support streaming, it will have to buffer for that plugin and it's all moot.
  2. I'm ok with doing a major release for this.

I would prefer no option for this. We can mention streaming support and how to use it gulp.src(..., {buffer: false}).

Copy link
Contributor Author

@gpoole gpoole Aug 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's uncommon but definitely possible to have multiple modes on the Vinyl stream aside from plugin support. This example is a bit contrived, but I could do:

const mergeStream = require('merge-stream');
const gulp = require('gulp');
const gulpZip = require('gulp-zip');

exports.default = function() {
  return mergeStream(
    gulp.src('*.mp4', { buffer: false }),
    gulp.src('*.txt'),
  )
  .pipe(gulpZip())
  .pipe(gulp.dest('out/myzip.zip'));
}

I think I'd have a race condition where I'll either get a streamed or a buffered zip depending on which file arrives first, which I don't think should happen, and that also means I can't force streaming on if I'm having the oversized buffer problem.

What about instead if I changed it so that an oversized buffer error automatically switches the output to streaming and I'll add a note about it to the readme? Then we could avoid the option and automatically handle the error.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot automatically switch to streaming as it affects the plugins coming after this one.

I think you should rather bring this up on the Gulp/Vinyl issue tracker. Plugins should not be opinionated about this. They should support either buffer or both and adhere to the mode Gulp tells them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the long delay on responding here. Yeah I agree that auto switching is the wrong way to go and would be confusing.

The problem I have with doing it automatically is that the file contents being buffered or streams is a property of each Vinyl file, but not of the stream as a whole. There's no API to check the streaming/buffered mode of the whole stream and it's possible to construct valid streams with mixed content, so my interpretation is that this is an intentional and quite useful feature of Gulp. I believe that plugins need to respect the mode of each file in the stream and it's not correct to infer the nature of every file in the stream from one instance. If I change this to automatically switch mode based on the first file it will introduce inconsistent behavior for mixed content streams like the one I mentioned above (the output could alternate between streaming and buffered depending on which file comes through the pipe first).

I think that given that, my opinion is that an option is really the only consistent and safe way to handle this. It's unambiguous and discoverable and gives the users of the plugin the choice without introducing the potential for inconsistent behaviour.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like an option is the only choice, yes, but I also think your use-case is very obscure, and that it would be easier for you to just make everything streaming. 🤷🏻‍♂️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it isn't popular to do that, but I think that's a shame because in my opinion it's one of the coolest Gulp/Vinyl features that the API is based on regular streams and for example merging and splitting can be done at the stream level without Gulp even needing to know or provide APIs for it. I think it's a very powerful feature! That's why I think it's worth us supporting it with this change.

So if I put the option back in would you be ok with that?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if I put the option back in would you be ok with that?

Yes, but it should be clearly documented in what case the option is useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok the option is back but renamed to buffer to match the gulp.src buffer option which I hope should make it easy to interpret along with the docs.

index.js Outdated Show resolved Hide resolved
@gpoole
Copy link
Contributor Author

gpoole commented Jan 4, 2021

I'll investigate the failing Node 14 test when I've got more time later this week.

@gpoole gpoole changed the title Add support for streaming output with streamOutput option Add support for streaming output with option Jan 4, 2021
readme.md Outdated
If `true`, the resulting ZIP file contents will be a buffer. Large zip files may not be possible to buffer, depending on the size of [Buffer MAX_LENGTH](https://nodejs.org/api/buffer.html#buffer_buffer_constants_max_length).
If `false`, the ZIP file contents will be a stream.

Similar to [gulp.src's `buffer` option](https://gulpjs.com/docs/en/api/src/#options).
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but it should be clearly documented in what case the option is useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully clarified that now.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not really clearer. As a user, I would have liked to know why the option exists when there's the gulp.src's buffer option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I felt that was explained in the paragraph above. Do you want me to remove the comparison or add more detail here?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add more detail.

Base automatically changed from master to main January 23, 2021 12:04
readme.md Outdated
@@ -61,3 +61,5 @@ If `true`, the resulting ZIP file contents will be a buffer. Large zip files may
If `false`, the ZIP file contents will be a stream.

Similar to [gulp.src's `buffer` option](https://gulpjs.com/docs/en/api/src/#options).

Tip: set this to `false` to allow creating ZIP files larger than Node's maximum buffer length.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just repeating what's on line 60. I think the option docs should explain why it exists when there's already such option for gulp.src.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would something like this work:

We use this option instead of relying on gulp.src's buffer option because we are mapping many input files to one output file and can't reliably detect what the output mode should be based on the inputs, since Vinyl streams could contain mixed streaming and buffered content.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's good.

@sindresorhus
Copy link
Owner

Bump

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants