jimmywarting / StreamSaver.js

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

Cannot pipe to a locked stream. #120

Closed rachitpant closed 5 years ago

rachitpant commented 5 years ago

I am trying to use it in a quasar app ( Vue components for cordova app ). Here is my code. I am getting a cannot pipe to a locked stream error message.

 downloadFile (key) {
      axios
        .get(
          some url,
          {
            responseType: 'stream',
            params: {
              key
            }
          }
        )
        .then(response => {
          console.log('The response was ' + response.data)
          const fileStream = streamSaver.createWriteStream('somefile.png')

          window.writer = fileStream.getWriter()
          const readableStream = new Response(
            response.data
          ).body
          if (window.WritableStream && readableStream.pipeTo) {
            return readableStream.pipeTo(fileStream)
              .then(() => console.log('done writing'))
          }
          // Write (pipe) manually
          const writer = fileStream.getWriter()
          const reader = readableStream.getReader()
          const pump = () => reader.read()
            .then(res => res.done
              ? writer.close()
              : writer.write(res.value).then(pump))
          pump()
        })
        .catch(error => {
          console.log('There was an error:', error.response)
        })
    }

Inside the if block. What am I doing wrong.

rachitpant commented 5 years ago

Also noticed , the fileStream is momentarily not locked. image

rachitpant commented 5 years ago

Looks like window.writer = fileStream.getWriter() is causing the fileStream to become locked. I removed that line and now it seems to be working and I get 'done writing' on the console , but there are two major issues issues. 1) I cannot find the file in my android emulator 2) Sometimes I see a browser opening from my app , with a url of this repo

jimmywarting commented 5 years ago

you shouldn't use axios togheter with streamsaver as you don't get any whatwg Stream. it only works in node... the way you are doing it now is fetching all data and buffer up everything in memory, in that case you can just use responseType blob and use fileSaver for example. beets the hole point of using this lib.

use fetch directly instead of wrapping the data in a Response

the lock problem is b/c you are calling it twice you should get the writer when you manually want to write to it. if you use pipe then avoid it.

         // You are calling this line twice: <--------
         window.writer = fileStream.getWriter()
         const readableStream = new Response( // <----- use fetch directly instead
           response.data
         ).body
         if (window.WritableStream && readableStream.pipeTo) {
           return readableStream.pipeTo(fileStream)
             .then(() => console.log('done writing'))
         }
         // Write (pipe) manually
         // Here is the other line: <---------
         const writer = fileStream.getWriter()
         const reader = readableStream.getReader()
         const pump = () => reader.read()
           .then(res => res.done
             ? writer.close()
             : writer.write(res.value).then(pump))
         pump()
  1. can't help you there.
  2. it's b/c it uses service worker to emulate what a server dose. StreamSaver was built with easier setup, So that developer don't have to configure a service worker themself and use the streamsaver lib directly from jsfiddle, npm and even insecure webpages that don't have https. setting up service worker and manage updates/cache isn't always the easiest task.

if you want, you can host the service worker and mitm yourself on your own domain then you can configure the mitm to your own address StreamSaver.mitm = 'https://example.com/mitm.html'

i say this many times, if you have a file that's coming from the server and you have control over it, do respond with a content-disposition attachment header and avoid all the javascript shenanigans. there are 3 ways you can do it: