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

[New API] Send stream to WICG/native-filesystem api #73

Open jimmywarting opened 5 years ago

jimmywarting commented 5 years ago

This could be a useful api in the feature.

https://github.com/WICG/native-filesystem/blob/master/EXPLAINER.md

Then you wouldn't have to use a service worker, which is a huge +

jimmywarting commented 5 years ago

I played with it a bit in chrome canary. when trying out chooseFileSystemEntries({type:'saveFile'})

jimmywarting commented 4 years ago

Tada: https://github.com/jimmywarting/native-file-system-adapter

tomayac commented 4 years ago

Tada: https://github.com/jimmywarting/native-file-system-adapter

We offer a similar library: browser-nativefs. Its API deliberately differs from the native API.

tomayac commented 4 years ago

Do you have plans on adding Native File System API support to StreamSaver.js? If so, happy to help.

jimmywarting commented 4 years ago

Saw you lib after i uploaded my adapter to the public yours is more like a shim than a polyfill or ponyfill (but without the sandboxing features)

Do you want to cross references similar projects for publicity? :)


I have had plans on adding native fs to streamsaver if only it where possible to include a file name https://github.com/WICG/native-file-system/issues/80 but it isn't so i rather prefer the current method of using content-disposition + service worker solution for a better user experience. It will also allow things such as auto download on user interaction without prompting. and show a native progress indication with the option to cancel the download.

the way you save things with the native fs diverge from the way you do it with StreamSaver with native fs you first ask for permission, select a path, name and file format then you are allowed to write to the destination StreamSaver just assumes everything is all fine and dandy and starts writing right away.

I don't know how i should fit native fs into streamsaver anymore. if you have any ideas then i'm all ears and also willing to review some PR.


If i started a new project and I needed to save files then i would not use StreamSaver anymore. my adapter is just as compatible to download stuff without the need of a MITM and dose a better job at it also.

You can test the manual download here: https://jimmywarting.github.io/native-file-system-adapter/example/test.html

Here is ruffly the extracted save method from the test file

// get some dummy gradient image
function img (format) {
  var a = document.createElement("canvas"),
      b = a.getContext("2d"),
      c = b.createLinearGradient(0, 0, 1500, 1500);
  a.width = a.height = 3000;
  c.addColorStop(0, "red");
  c.addColorStop(1, "blue");
  b.fillStyle = c;
  b.fillRect(0, 0, a.width, a.height);
  return new Promise(rs => {
    a.toBlob(rs, 'image/' + format, 1)
  })
}

download.onclick = async () => {
  const opts = {
    type: 'save-file',
    accepts: [
      { extensions: ['jpg'] },
      { extensions: ['webp'] },
      { mimeTypes: ['image/png'] }
    ],
    excludeAcceptAllOption: true,
    _name: 'image.jpg', // fallback if not using native file system
    _preferPolyfill: false // default is false
  }
  // Will either use 
  // 1. native file system if it is supported
  // 2. streamsaver.js method of using a service worker (if a service worker is installed)
  // 3. or lastly fallback to a temporary memory blob builder and use `a[download]`
  const handle = await chooseFileSystemEntries(opts)
  // figure out the format use wants
  const format = handle.name.split('.').pop()
  // generate a blob image from canvas
  const image = await img(format) 
  const ws = await handle.createWritable()
  // write blob
  ws.write(image)
  ws.close()
}
tomayac commented 4 years ago

Saw you lib after i uploaded my adapter to the public yours is more like a shim than a polyfill or ponyfill (but without the sandboxing features)

True, and deliberately so. It adds some convenience features on top, for example, directory browsing.

Do you want to cross references similar projects for publicity? :)

Just did: https://github.com/GoogleChromeLabs/browser-nativefs/blob/master/README.md#alternatives.

I have had plans on adding native fs to streamsaver if only it where possible to include a file name WICG/native-file-system#80

Yeah, hope this is coming soon.

but it isn't so i rather prefer the current method of using content-disposition + service worker solution for a better user experience. It will also allow things such as auto download on user interaction without prompting. and show a native progress indication with the option to cancel the download.

Not sure about your exact use cases, but an interesting alternative might be background fetch.

the way you save things with the native fs diverge from the way you do it with StreamSaver with native fs you first ask for permission, select a path, name and file format then you are allowed to write to the destination StreamSaver just assumes everything is all fine and dandy and starts writing right away.

I see, thanks.

I don't know how i should fit native fs into streamsaver anymore. if you have any ideas then i'm all ears and also willing to review some PR.

If i started a new project and I needed to save files then i would not use StreamSaver anymore. my adapter is just as compatible to download stuff without the need of a MITM and dose a better job at it also.

Agree, there are alternatives now. It was a great solution, though.

You can test the manual download here: https://jimmywarting.github.io/native-file-system-adapter/example/test.html

That's impressive.

Here is ruffly the extracted save method from the test file

// get some dummy gradient image
function img (format) {
  var a = document.createElement("canvas"),
      b = a.getContext("2d"),
      c = b.createLinearGradient(0, 0, 1500, 1500);
  a.width = a.height = 3000;
  c.addColorStop(0, "red");
  c.addColorStop(1, "blue");
  b.fillStyle = c;
  b.fillRect(0, 0, a.width, a.height);
  return new Promise(rs => {
    a.toBlob(rs, 'image/' + format, 1)
  })
}

download.onclick = async () => {
  const opts = {
    type: 'save-file',
    accepts: [
      { extensions: ['jpg'] },
      { extensions: ['webp'] },
      { mimeTypes: ['image/png'] }
    ],
    excludeAcceptAllOption: true,
    _name: 'image.jpg', // fallback if not using native file system
    _preferPolyfill: false // default is false
  }
  // Will either use 
  // 1. native file system if it is supported
  // 2. streamsaver.js method of using a service worker (if a service worker is installed)
  // 3. or lastly fallback to a temporary memory blob builder and use `a[download]`
  const handle = await chooseFileSystemEntries(opts)
  // figure out the format use wants
  const format = handle.name.split('.').pop()
  // generate a blob image from canvas
  const image = await img(format) 
  const ws = await handle.createWritable()
  // write blob
  ws.write(image)
  ws.close()
}

This code feels familiar :-)

jimmywarting commented 4 years ago

Just did

Me 2

but an interesting alternative might be background fetch.

Nah, StreamSaver is not about fetching any resource. it's more about having the possibility to write to a writable stream, you can't do that when it's happening in the background.