jimmywarting / StreamSaver.js

StreamSaver writes stream to the filesystem directly asynchronous
https://jimmywarting.github.io/StreamSaver.js/example.html
MIT License
3.98k stars 413 forks source link

Usage With Webpack And ES6 Imports #198

Open MannikJ opened 3 years ago

MannikJ commented 3 years ago

I am trying to use this package to download a ZIP file consisting of multiple files stored on AWS S3. My app is based on Laravel and uses Laravel Mix for asset compilation and bundling. I installed streamsaver and web-streams-polyfill/ponyfill via npm and tried to create a js bundle with the code to be imported into a blade view (actually a Livewire component view). Inside my script file I try to include the sources with ES6 imports and that's where the problem arises I guess, because they behave different than if I would just include script tags.

My implementation works in Chrome as long as I don't import the stream ponyfill (at least the way I do it) but not in Firefox. The code is based on the zip example from the docs which is working in Chrome and Firefox.

This it what works in chrome:

import streamSaver from "streamsaver";
import "streamsaver/examples/zip-stream";
// import * as ponyfill from "web-streams-polyfill/ponyfill";
// streamSaver.WritableStream = ponyfill.WritableStream;

Livewire.on("download", (files, fileName) => {
  let handler = function(event) {
    event.preventDefault();
    event.returnValue = "";
  };

  window.addEventListener("beforeunload", handler);

  const fileStream = streamSaver.createWriteStream(fileName);
  const readableZipStream = new ZIP({
    start(ctrl) {},
    async pull(ctrl) {
      // Gets executed everytime zip.js asks for more data
      let file = files.shift();
      const res = await fetch(file.url);
      const stream = () => res.body;
      const name = file.name;
      ctrl.enqueue({
        name,
        stream,
      });
      if (files.length === 0) ctrl.close();
    },
  });

  if (window.WritableStream && readableZipStream.pipeTo) {
    return readableZipStream.pipeTo(fileStream).then(() => {
      window.removeEventListener("beforeunload", handler);
    });
  }

  pump();
});

However in Firefox I get Uncaught (in promise) TypeError: streamSaver.WritableStream is not a constructor without the ponyfill but when I uncomment the two lines, another error occurs (Uncaught (in promise) ReferenceError: pump is not defined) and it doesn't work in Chrome anymore: Uncaught (in promise) TypeError: Failed to execute 'pipeTo' on 'ReadableStream': Illegal invocation.

When I change import "streamsaver/examples/zip-stream"; to import {pump} from "streamsaver/examples/zip-stream";, i get Uncaught (in promise) TypeError: Object(...) is not a function in Firefox..

So as you see I just cannot make it work for both browser not matter what.

I know its probably an obvious mistake and not really a problem with this library, but I would be glad for any advice though.

EtienneBruines commented 3 years ago

@MannikJ You might have luck by adding this before your pump() call


    // less optimized
    const writer = fileStream.getWriter()
    const reader = readableZipStream.getReader()
    const pump = () => reader.read()
      .then(res => res.done ? writer.close() : writer.write(res.value).then(pump))

    pump()