镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Streaming compatibility with archiver #671

Closed

Description

@danhaller

Hi,

I'm trying to use sharp's streaming capability in tandem with archiver to resize and zip multiple images and stream out to a consumer. I can get each library to work on their own, but not together on any more than a single image.

Here's some example code:

const fs = require('fs');
const archiver = require('archiver');
const sharp = require('sharp');
//const imagemagick = require('imagemagick-stream');
const request = require('request');

function resizeAndZip() {
  const imageStreams = [
    request('http://i.stack.imgur.com/tKsDb.png'),
    request('http://i.stack.imgur.com/EdUwb.png'),
    request('http://i.stack.imgur.com/5d55j.png')
  ];

  const zip = archiver('zip');

  zip.on('entry', (entry) => console.log(`${entry.name} successfully appended to zip`));

  imageStreams.map((stream) => {
    const resize = sharp().resize(100, 100);
    // const resize = imagemagick().resize('100x100');

    return stream.pipe(resize);
  })
  .forEach((resizedImageStream, i) => {
    zip.append(resizedImageStream, { name: `${i}.png` });
  });

  zip.finalize();

  return zip;
}

resizeAndZip().pipe(fs.createWriteStream(__dirname + '/output.zip'));

In the example you can see that I've also imported imagemagick-stream. If this library is used as the resizer instead of sharp everything works fine. I'm not sure whether this is a compatibility problem or if I'm using sharp incorrectly.

Activity

  1. lovell commented on Jan 6, 2017

    @lovell
    Owner

    Hello, I think the resizedImageStream instances have yet to enter flowing mode. This happens automagically when something else pipes data out, but archiver doesn't appear to do this for you.

    Something like the following should work:

        zip.append(resizedImageStream.resume(), { name: `${i}.png` });

    Perhaps sharp's constructor jsdoc should include this information? I'd be happy to accept a PR if you'd like to make the docs clearer.

  2. danhaller commented on Jan 9, 2017

    @danhaller
    Author

    Thanks for the reply. Adding resume is definitely an improvement, the zip file now completes with all the files. It appears that most of the files are corrupt, however, when the archive is unzipped. I'm trying not to get too stackoverflow here but is this likely to be a sharp issue or an archiver issue?

    I'll definitely add a PR if you think it would be useful. Perhaps it would be more useful in archiver though, as I imagine most people using sharp will be using pipe?

  3. lovell commented on Jan 9, 2017

    @lovell
    Owner

    I can partially recreate this - the first two images in the zip file are OK but the third/last image has zero length.

    To investigate further we'd need to look at the internals of the archiver module to see what custom logic it uses to handle a ReadableStream instance vs what Node gives you for "free" when using pipe.

  4. lovell commented on Jan 17, 2017

    @lovell
    Owner

    I think I might have identified a race condition where the Writable side of a sharp instance can emit the "finish" event before the Readable side has started flowing. My next step is to isolate it to a repeatable test case.

    (I had a quick look at the archiver code and it does call pipe internally in a sequential, queued manner. My advice to resume a Readable stream before calling zip.append is probably wrong.)

  5. lovell commented on Jan 22, 2017

    @lovell
    Owner

    Commit d8df503 adds a test and the fix for the case where the Readable side of the Duplex Stream starts flowing only after the Writable side of the Duplex Stream has emitted the "finish" event. Thanks for reporting this.

  6. added this to the v0.17.2 milestone on Jan 22, 2017
  7. added and removed on Jan 22, 2017
  8. sinegovsky-ivan commented on Jan 27, 2017

    @sinegovsky-ivan

    The same issue with fix from "Commit d8df503" - 90% images have zero size :(

  9. lovell commented on Jan 27, 2017

    @lovell
    Owner

    @danhaller Have you been able to verify the proposed fix?

  10. danhaller commented on Jan 27, 2017

    @danhaller
    Author

    @lovell Thanks for investigating this and sorry I haven't got back to you sooner - busy week. I've just tried the latest commit with the code example above and I'm still seeing the same problem unfortunately - the first two images are zipped correctly but any others are not.

  11. lovell commented on Jan 27, 2017

    @lovell
    Owner

    Do you still see the problem if you remove the finalize call from resizeAndZip and instead call it after you've started piping data to the filesystem?

    ...
    const zip = resizeAndZip();
    zip.pipe(fs.createWriteStream(__dirname + '/output.zip'));
    zip.finalize();
  12. danhaller commented on Jan 27, 2017

    @danhaller
    Author

    Ah I forgot to remove the resume. After removing that it works correctly. I've also tried it out in the actual service that I'm writing and it works there too.

    Thanks again for fixing.

  13. lovell commented on Jan 27, 2017

    @lovell
    Owner

    Thanks @danhaller.

    @sinegovsky-ivan if you're still having problems, please open a new issue with a code sample that fails, preferably with error handling/logging on the streams.

  14. sinegovsky-ivan commented on Jan 28, 2017

    @sinegovsky-ivan

    I also removed "resume" and all ok now.

    Thank you for your work!

  15. rn4391 commented on Feb 7, 2017

    @rn4391

    If this is complete and working fine, then 0.17.2 can be released?

  16. lovell commented on Feb 11, 2017

    @lovell
    Owner

    v0.17.2 now available via npm, thanks for reporting this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions