StreamSaver.js icon indicating copy to clipboard operation
StreamSaver.js copied to clipboard

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

Open jimmywarting opened this issue 7 years ago • 7 comments

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 avatar Nov 14 '18 12:11 jimmywarting

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

  • i could not found a way to set the filename :(

jimmywarting avatar Aug 14 '19 06:08 jimmywarting

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

jimmywarting avatar Mar 05 '20 17:03 jimmywarting

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 avatar Jun 11 '20 09:06 tomayac

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

tomayac avatar Jun 11 '20 09:06 tomayac

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()
}

jimmywarting avatar Jun 11 '20 11:06 jimmywarting

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 :-)

tomayac avatar Jun 12 '20 10:06 tomayac

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.

jimmywarting avatar Jun 12 '20 11:06 jimmywarting