All Projects → jimmywarting → Streamsaver.js

jimmywarting / Streamsaver.js

Licence: mit
StreamSaver writes stream to the filesystem directly asynchronous

Programming Languages

javascript
184084 projects - #8 most used programming language
HTML
75241 projects

Projects that are alternatives of or similar to Streamsaver.js

Fetch Progress Indicators
Progress indicators/bars using Streams, Service Workers, and Fetch APIs
Stars: ✭ 181 (-93.5%)
Mutual labels:  stream, service-worker
Hls.js
HLS.js is a JavaScript library that plays HLS in browsers with support for MSE.
Stars: ✭ 10,791 (+287.61%)
Mutual labels:  stream, html5
Png Pong
An image manipulation library with a very specific set of skills.
Stars: ✭ 190 (-93.18%)
Mutual labels:  service-worker
Fastcast
🌊 Stream peer-to-peer audio and video content
Stars: ✭ 202 (-92.74%)
Mutual labels:  stream
Vlc Bittorrent
A bittorrent plugin for VLC.
Stars: ✭ 198 (-92.89%)
Mutual labels:  stream
Javascript
刘宾的博客->前端相关。
Stars: ✭ 195 (-93%)
Mutual labels:  html5
Gnome Shell Extension Cast To Tv
Cast files to Chromecast, web browser or media player app over local network.
Stars: ✭ 200 (-92.82%)
Mutual labels:  stream
Mindmaps
An open source, offline capable, mind mapping application leveraging HTML5 technologies
Stars: ✭ 2,339 (-15.98%)
Mutual labels:  html5
Copilot
A stream-based runtime-verification framework for generating hard real-time C code.
Stars: ✭ 204 (-92.67%)
Mutual labels:  stream
Pristine
Vanilla javascript form validation micro-library
Stars: ✭ 197 (-92.92%)
Mutual labels:  html5
Blockchain
A simple implementation of blockchain in java
Stars: ✭ 201 (-92.78%)
Mutual labels:  html5
Amazonriver
amazonriver 是一个将postgresql的实时数据同步到es或kafka的服务
Stars: ✭ 198 (-92.89%)
Mutual labels:  stream
Ovenplayer
OvenPlayer is Open-Source HTML5 Player. OvenPlayer supports WebRTC Signaling from OvenMediaEngine for Sub-Second Latency Streaming.
Stars: ✭ 196 (-92.96%)
Mutual labels:  html5
Chimee
a video player framework aims to bring wonderful experience on browser
Stars: ✭ 2,332 (-16.24%)
Mutual labels:  html5
The Front End Knowledge You May Not Know
😇 你可能不知道的前端知识点
Stars: ✭ 2,238 (-19.61%)
Mutual labels:  html5
Firebase Esp32
ESP32 Firebase RTDB Arduino Library
Stars: ✭ 204 (-92.67%)
Mutual labels:  stream
Xstream
An extremely intuitive, small, and fast functional reactive stream library for JavaScript
Stars: ✭ 2,259 (-18.86%)
Mutual labels:  stream
Stisla
Free Bootstrap Admin Template
Stars: ✭ 2,772 (-0.43%)
Mutual labels:  html5
Kinesis Consumer
Golang library for consuming Kinesis stream data
Stars: ✭ 198 (-92.89%)
Mutual labels:  stream
One Html Page Challenge
Can you create something cool without modern tools?
Stars: ✭ 205 (-92.64%)
Mutual labels:  html5

StreamSaver.js (legacy-ish)

... Don't worry it's not deprecated. It's still maintained and i still recommend using this when needed. Just want to let you know that there is this new native way to save files to the HD: https://wicg.github.io/native-file-system which is more or less going to make FileSaver, StreamSaver and similar packages a bit obsolete in the future, it'still in a experimental stage and not implemented by all browser. That is why I also built native-file-system-adapter so you can have it in all Browsers, Deno, and NodeJS with different storages

npm version

First I want to thank Eli Grey for a fantastic work implementing the FileSaver.js to save files & blobs so easily! But there is one obstacle - The RAM it can hold and the max blob size limitation

StreamSaver.js takes a different approach. Instead of saving data in client-side storage or in memory you could now actually create a writable stream directly to the file system (I'm not talking about chromes sandboxed file system or any other web storage). This is accomplish by emulating how a server would instruct the browser to save a file using some response header + service worker

StreamSaver.js is the solution to saving streams on the client-side. It is perfect for webapps that need to save really large amounts of data created on the client-side, where the RAM is really limited, like on mobile devices.

If the file you are trying to save comes from the cloud/server use the server instead of emulating what the browser does to save files on the disk using StreamSaver. Add those extra Response headers and don't use AJAX to get it. FileSaver has a good wiki about using headers. If you can't change the headers then you may use StreamSaver as a last resort. FileSaver, streamsaver and others alike are mostly for client generated content inside the browser.

Getting started

StreamSaver in it's simplest form

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ponyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/StreamSaver.min.js"></script>
<script>
  import streamSaver from 'streamsaver'
  const streamSaver = require('streamsaver')
  const streamSaver = window.streamSaver
</script>
<script>
  const uInt8 = new TextEncoder().encode('StreamSaver is awesome')

  // streamSaver.createWriteStream() returns a writable byte stream
  // The WritableStream only accepts Uint8Array chunks
  // (no other typed arrays, arrayBuffers or strings are allowed)
  const fileStream = streamSaver.createWriteStream('filename.txt', {
    size: uInt8.byteLength, // (optional filesize) Will show progress
    writableStrategy: undefined, // (optional)
    readableStrategy: undefined  // (optional)
  })

  if (manual) {
    const writer = fileStream.getWriter()
    writer.write(uInt8)
    writer.close()
  } else {
    // using Response can be a great tool to convert
    // mostly anything (blob, string, buffers) into a byte stream
    // that can be piped to StreamSaver
    //
    // You could also use a transform stream that would sit
    // between and convert everything to Uint8Arrays
    new Response('StreamSaver is awesome').body
      .pipeTo(fileStream)
      .then(success, error)
  }
</script>

Some browser have ReadableStream but not WritableStream. web-streams-polyfill can fix this gap. It's better to load the ponyfill instead of the polyfill and override the existing implementation because StreamSaver works better when a native ReadableStream is transferable to the service worker. hopefully MattiasBuelens will fix the missing implementations instead of overriding the existing. If you think you can help out here is the issue

Best practice

Use https if you can. That way you don't have to open the man in the middle in a popup to install the service worker from another secure context. Popups are often blocked but if you can't it's best that you initiate the createWriteStream on user interaction. Even if you don't have any data ready - this is so that you can get around the popup blockers. (In secure context this don't matter) Another benefit of using https is that the mitm-iframe can ping the service worker to prevent it from going idle. (worker goes idle after 30 sec in firefox, 5 minutes in blink) but also this won't mater if the browser supports transferable streams throught postMessage since service worker don't have to handle any logic. (the stream that you transfer to the service worker will be the stream we respond with)

Handle unload event when user leaves the page. The download gets broken when you leave the page. Because it looks like a regular native download process some might think that it's okey to leave the page beforehand since it's is downloading in the background directly from some a server, but it isn't.

// abort so it dose not look stuck
window.onunload = () => {
  writableStream.abort()
  // also possible to call abort on the writer you got from `getWriter()`
  writer.abort()
}

window.onbeforeunload = evt => {
  if (!done) {
    evt.returnValue = `Are you sure you want to leave?`;
  }
}

Note that when using insecure context StreamSaver will navigate to the download url instead of using an hidden iframe to initiate the download, this will trigger the onbefureunload event when the download starts, but it will not call the onunload event... In secure context you can add this handler immediately. Otherwise this has to be added sometime later.

Configuration

There a some few settings you can apply to StreamSaver to configure what it should use

// StreamSaver can detect and use the Ponyfill that is loaded from the cdn.
streamSaver.WritableStream = streamSaver.WritableStream
streamSaver.TransformStream = streamSaver.TransformStream
// if you decide to host mitm + sw yourself
streamSaver.mitm = 'https://example.com/custom_mitm.html'

Examples

There are a few examples in the examples directory

In the wild

How does it work?

There is no magical saveAs() function that saves a stream, file or blob. (at least not if/when native-filesystem api becomes avalible) The way we mostly save Blobs/Files today is with the help of Object URLs and a[download] attribute FileSaver.js takes advantage of this and create a convenient saveAs(blob, filename). fantastic! But you can't create a objectUrl from a stream and attach it to a link...

link = document.createElement('a')
link.href = URL.createObjectURL(stream) // DOES NOT WORK
link.download = 'filename'
link.click() // Save

So the one and only other solution is to do what the server does: Send a stream with Content-Disposition header to tell the browser to save the file. But we don't have a server or the content isn't on a server! So the solution is to create a service worker that can intercept request and use respondWith() and act as a server.
But a service workers are only allowed in secure contexts and it requires some effort to put up. Most of the time you are working in the main thread and the service worker are only alive for < 5 minutes before it goes idle.

  1. So StreamSaver creates a own man in the middle that installs the service worker in a secure context hosted on github static pages. either from a iframe (in secure context) or a new popup if your page is insecure.
  2. Transfer the stream (or DataChannel) over to the service worker using postMessage.
  3. And then the worker creates a download link that we then open.

if a "transferable" readable stream was not passed to the service worker then the mitm will also try to keep the service worker alive by pinging it every x second to prevent it from going idle.

To test this locally, spin up a local server
(we don't use any pre compiler or such)

# A simple php or python server is enough
php -S localhost:3001
python -m SimpleHTTPServer 3001
# then open localhost:3001/example.html
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].