Range-Capable Bundle File Streaming

How to request bundle file URLs that support HTTP byte ranges for point-cloud and large-file viewers.

Bundle file download endpoints return presigned object-storage URLs by default. Those URLs are best for normal whole-file downloads.

Some viewers, such as point-cloud streamers, need Range requests so they can load only the required byte windows. For those clients, use the dedicated range-download endpoints. They return presigned object-storage URLs with supportsRangeRequests: true, so byte-range traffic goes directly to storage instead of through PixService API rate limiting.

Request stream URLs

GET /v1/spatial/bundles/{bundleId}/files/range-download?disposition=inline
Authorization: Bearer <personal-access-token>

The response has the same paging shape as the normal download endpoint. Each item uses:

  • delivery: "presigned"
  • supportsRangeRequests: true
  • url: a short-lived object-storage GET URL
  • expiresAt: when the object-storage signature expires

Use the same url for each byte window and change the HTTP Range header for each request. Do not edit, remove, or append signed query parameters to request a different range; object-storage signatures cover the query string and a modified URL may be rejected.

For Potree integrations, keep non-range files such as metadata JSON on regular presigned URLs. Use range-download URLs for the binary resources that Potree requests with a Range header.

Set the range

The byte window is set on the storage request, not on the PixService request and not by changing the returned URL:

const response = await fetch(download.url, {
  headers: {
    Range: `bytes=${startByte}-${endByte}`,
  },
});

Equivalent curl smoke test:

curl -i -H "Range: bytes=0-1048575" "<returned-url>"

The storage response should be 206 Partial Content and include a Content-Range header. A 200 OK response means the storage endpoint or a proxy ignored the Range header and returned the full object.

Stream with Range

GET https://storage.example.test/presigned-object-url
Range: bytes=0-1048575

Expected response:

HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Range: bytes 0-1048575/123456789
Content-Type: application/octet-stream

If no Range header is sent, object storage returns the whole object. If the range is malformed or outside the file size, object storage returns its native range error response.

Browser-based viewers need object-storage CORS to allow the Range request header and expose response headers such as Accept-Ranges, Content-Range, Content-Length, and ETag.

Example S3 CORS rule:

<CORSConfiguration>
  <CORSRule>
    <AllowedOrigin>https://atlas.pixgruppe.de</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedHeader>Range</AllowedHeader>
    <ExposeHeader>Accept-Ranges</ExposeHeader>
    <ExposeHeader>Content-Range</ExposeHeader>
    <ExposeHeader>Content-Length</ExposeHeader>
    <ExposeHeader>ETag</ExposeHeader>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
  </CORSRule>
</CORSConfiguration>

For development and staging, use the matching Atlas origin for that environment. If object storage is behind a reverse proxy, the proxy must forward Range and CORS preflight requests to the S3 gateway or add equivalent CORS headers itself.

SeaweedFS compatibility

SeaweedFS S3 range behavior depends on the deployed SeaweedFS version and proxy path. Before enabling direct range URLs for a viewer, verify the exact public storage endpoint with the curl smoke test above. The important checks are:

  • 206 Partial Content, not 200 OK
  • Content-Range matches the requested byte window
  • Content-Length matches the requested range length
  • repeated range requests do not cause unexpected full-object reads or excessive volume-server traffic

If those checks fail, keep using the legacy PixService /content API range endpoint until the storage layer is fixed or upgraded.

Single file

To get one range-capable URL:

GET /v1/spatial/bundles/{bundleId}/files/{artifactId}/range-download?disposition=inline
Authorization: Bearer <personal-access-token>

Use /files/download and /files/{artifactId}/download for regular downloads. Use the range-download endpoints only when byte-range reads are required.

On this page