jimmywarting / StreamSaver.js

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

First Save the file and then write to it. #311

Closed Tzvetelin88 closed 1 year ago

Tzvetelin88 commented 1 year ago

Can we fist popup the screen on the Browser to tell where we want to save the file on the Client Side and then start write to it?

I first get data streamed to the Client, and few seconds later (even sometimes at the end) browser popups the screen to save the file on the Clients FS. I started this project with SocketIO.

In short I have this. Creating writeStream -> init getWriter -> socket emit "fileDownload" which creates createReadStream and sending chunks back to the client and client listen to them on "fileProgress".

 const fileStream = streamSaver.createWriteStream(this.fileToDownload.name, {
      size: this.fileToDownload.size, // (optional filesize) Will show progress
    });
    this.fStream = fileStream.getWriter();

    this.socket.emit('fileDownload', {
      fileName: this.fileToDownload.name,
    });

    this.socket.on(
      'fileProgress',
      (data: { percentage: any; bufferData: any }) => {
        this.downloadPercent = parseInt(data.percentage);
        this.fStream.write(new Uint8Array(data.bufferData));
      }
    );
jimmywarting commented 1 year ago

No, it's not possible. to initiate the save dialog you actually have to start the download first. It's however possible with the new file system access api await showSaveFilePicker() but it's only avalible in Blink

Tzvetelin88 commented 1 year ago

I was experimenting and did "write: immediately after "getWriter" and then browser first displayed screen to save the file and then started to write(where as in the example above SockerIO is async and there is a time frame between "write" and "getWriter"), is this possible?

Example:

const fileStream = streamSaver.createWriteStream('test.txt', {
      size: this.fileToDownload.size, // (optional filesize) Will show progress
    });
    this.fWriter = fileStream.getWriter();

    this.fWriter.write(
      new Uint8Array([
        84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 85, 105, 110, 116, 56, 65,
        114, 114, 97, 121, 32, 99, 111, 110, 118, 101, 114, 116, 101, 100, 32,
        116, 111, 32, 97, 32, 115, 116, 114, 105, 110, 103,
      ])
    );
    this.fWriter.close();

P.S. I was thinking how for example OneDrive or GoogleDrive actually saving the files. They first ask to save file on Client side and then start writing.

Tzvetelin88 commented 1 year ago

Actually I found why above "seems to work". Data is too small, data is already written to memory and in the end browser saveAs is displayed, so yes... the same problem as firstly noticed.

Is this what you have mentioned - https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker? Seems Chrome have it? In the dev console of Chrome, when i do showSaveFilePicker() it actually shows it

Tzvetelin88 commented 1 year ago

I did one quick implementation and seem to work. @jimmywarting let me know what do you think.

Code updated like this:

try {
      // Show the file save dialog.
      const handle = await window.showSaveFilePicker({
        suggestedName: 'tes1200.txt',
      });

      // Write the blob to the file.
      this.fWriter = await handle.createWritable();
    } catch (err: any) {
      // Fail silently if the user has simply canceled the dialog.
      if (err.name !== 'AbortError') {
        console.error(err.name, err.message);
        return;
      }
    }

//Socket io section to stream data and use this.fWriter
await this.fWriter.write(new Uint8Array(data.bufferData))

// after file is streamed we close the stream
await this.fWriter.close()

I needed to install

yarn add -D @types/wicg-file-system-access

and to add

"types": ["@types/wicg-file-system-access"]

to "types": ["@types/wicg-file-system-access"]

https://user-images.githubusercontent.com/9692941/225993997-5cbf274e-3421-4bc1-9b99-b90dce5eecd8.mov

I attached a video.

jimmywarting commented 1 year ago

Looks alright