[w3c/FileAPI] Proposal: `Blob.from()` for creating virtual Blobs with custom backing storage (Issue #209)

jimmywarting created an issue (w3c/FileAPI#209)

I'd like to propose an addition to the Blob API to enable the creation of virtual Blob or File instances backed by custom-defined storage logic. This would allow SDKs and libraries to expose file-like objects without requiring the developer to manage low-level data fetching or streaming themselves.

### ✨ Proposed API

```js
const virtualBlob = Blob.from({
  size: 1024,
  async stream(start, end) {
    return customStreamOrAsyncIterable(start, end)
  }
})
```

- size: Total size of the blob (required).

- `stream(start, end)`: Required method that returns a ReadableStream or AsyncIterable<Uint8Array> for the requested byte range. it may or may not be async

This API is synchronous to create, but lazy in that no data is fetched until actually needed. The internal Blob machinery would take care of slicing and offsetting, so the consumer only implements the backing source logic.

### 🧩 Example Usage with SDK

This enables a clean integration pattern with APIs like Dropbox, Google Drive, or internal systems:

```js
import Client from 'dropbox/sdk.js'

const dropbox = new Client(apiKey)

const fileHandle = await dropbox.getFileHandle(user, path)

const file = fileHandle.openAsFile()
const url = URL.createObjectURL(file)
```

What dropbox sdk then actually dose:
```js
function openAsFile() {
  const blobPart = Blob.from({
    size: this.size,
    async stream(start, end) {
      return this._streamBytes(start, end)
    }
  })
  
  return new File([ blobPart ], this.filename, {
    type: this.type,
    lastModified: this.lastModified
  })
}
```

In this model:

- `fileHandle` contains only metadata (filename, type, size, lastModified).
- `openAsFile()` constructs a File backed by virtual Blob data.
- No actual data is fetched until the Blob is consumed — for example, when writing to disk or calling .arrayBuffer().

### ✅ Benefits

- Enables SDKs to expose virtual File/Blob objects without requiring developers to build ad-hoc wrappers.
- Avoids early data fetching and defers I/O until absolutely needed.
- Keeps the interface clean, idiomatic, and interoperable with existing Blob consumers.
- Great fit for use cases like remote file systems, zip file introspection, lazy file generation, and more.

### 🔧 Comparison to Today
Without this feature, developers must manually wrap streams, manage slicing, and emulate Blob behavior — often with duplicated effort and edge-case bugs. This addition would make such patterns first-class citizens in the platform.

### 🔍 Real-World Problem This Solves
A common pattern on the web is to trigger file downloads using a Blob URL and a programmatically clicked `<a>` tag:
```js
const blob = new Blob([fileData], { type: 'application/pdf' })
const url = URL.createObjectURL(blob)

const a = document.createElement('a')
a.href = url
a.download = 'report.pdf'
a.click()
```

However, to use this pattern today, you must already have all the file data in memory.

If you're working with a remote file (e.g. from cloud storage or an SDK), you can't delay the download until after the click — because:

Once the click handler ends, `isTrusted` becomes `false`.

Any async operation (like fetching the file) that happens after the click ends is now treated as not user-initiated.

Browsers will block the download, thinking it's an automatic or malicious attempt.

> ⛔️ This effectively means: if you want to allow the user to download a file, you must download the entire file first, even if they never end up clicking “Download.”

With `Blob.from()`, we could instead return a virtual Blob that doesn't require any data until it's needed:
```js
const blob = Blob.from({
  size: 10_000_000,
  async stream(start, end) {
    return fetchRangeStream(start, end)
  }
})

const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'report.pdf'
a.click()
```

In this model, the download is triggered immediately within the `click` event — but data is only fetched _as it's needed_, safely within the user gesture's trusted scope.

✅ This allows you to:

- Keep memory usage low (no preloading needed).
- Allow user-triggered downloads of remote/virtual files.
- Preserve compatibility with browser download restrictions.

-- 
Reply to this email directly or view it on GitHub:
https://github.com/w3c/FileAPI/issues/209
You are receiving this because you are subscribed to this thread.

Message ID: <w3c/FileAPI/issues/209@github.com>

Received on Monday, 19 May 2025 12:47:18 UTC