Skip to content

feat: range request support for getBytes and getStream#21

Merged
thetutlage merged 1 commit into
flydrive-js:2.xfrom
io-timeout:feat/range-requests
Jul 8, 2026
Merged

feat: range request support for getBytes and getStream#21
thetutlage merged 1 commit into
flydrive-js:2.xfrom
io-timeout:feat/range-requests

Conversation

@io-timeout

Copy link
Copy Markdown
Contributor

Range Reads

The getStream and getBytes methods support reading a subset of a file's bytes by passing an optional range option. This is useful for use cases like serving media segments, implementing resumable downloads, or reading file headers without fetching the entire file.

Basic usage

const disk = new Disk(driver)
 
const key = 'video.mp4'
 
/**
 * Read bytes 0 through 1023 (inclusive) as a stream
 */
const stream = await disk.getStream(key, {
  range: { start: 0, end: 1023 },
})
 
/**
 * Read the same range as a Uint8Array
 */
const bytes = await disk.getBytes(key, {
  range: { start: 0, end: 1023 },
})

Range options

The range option accepts a RangeRequest object with optional start and end properties.

Property Type Description
start number The byte position to start reading from (inclusive, 0-based)
end number The byte position to stop reading at (inclusive, 0-based)

Both start and end are absolute byte positions. They must be positive integers and are both inclusive. For example, { start: 0, end: 9 } reads the first 10 bytes of the file.

When start is omitted, reading begins from byte 0. When end is omitted, reading continues to the end of the file.

/**
 * Read from byte 1024 to the end of the file
 */
const stream = await disk.getStream(key, {
  range: { start: 1024 },
})
 
/**
 * Read from the beginning up to and including byte 511
 */
const stream = await disk.getStream(key, {
  range: { end: 511 },
})

Passing an empty range object ({}) or omitting the range option entirely both result in the full file being returned.

Validation and errors

Range values are validated before any I/O is performed. An E_RANGE_UNSATISFIABLE exception is thrown if:

  • start or end is a negative number
  • start or end is not an integer
  • start is greater than end
  • start or end exceeds the file size
import { errors } from 'flydrive'
 
try {
  const stream = await disk.getStream(key, {
    range: { start: 0, end: 99999 },
  })
} catch (error) {
  if (error instanceof errors.E_RANGE_UNSATISFIABLE) {
    // Range was out of bounds
  }
}

The validation behaviour is consistent across all three drivers (FS, GCS, and S3). A content length preflight is performed before the read in all cases to ensure uniform error handling regardless of the underlying storage backend.

Note on text content

Range reads are not supported on the get method, which returns file contents as a UTF-8 string. This is intentional, splitting a file at an arbitrary byte boundary can produce invalid UTF-8 sequences if a multi-byte character spans the boundary. Use getBytes with a range and decode the result yourself if you need ranged text reads and can guarantee boundary alignment.

/**
 * Read a range as bytes and decode manually
 */
const bytes = await disk.getBytes(key, {
  range: { start: 0, end: 99 },
})
 
const text = new TextDecoder('utf-8').decode(bytes)

@io-timeout

Copy link
Copy Markdown
Contributor Author

Hi @thetutlage. Could you please provide any feedback and your thoughts on when it might be possible to release this functionality? I work on a large project which uses flydrive and really needs this functionality. Thank you 👍

@thetutlage

Copy link
Copy Markdown
Collaborator

Looks good. Sorry for sleeping on it for a while. Can you also send a PR for the docs?

@thetutlage thetutlage merged commit c1bda83 into flydrive-js:2.x Jul 8, 2026
@io-timeout

Copy link
Copy Markdown
Contributor Author

Thanks for getting this merged @thetutlage! As requested, I've raised a PR with the documentation under flydrive-js/flydrive.dev#3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants