Streaming publication from a HTTP server
Introduction
The Colibrio reading system framework is designed to be "lazy" when working with publications.
Resources are loaded and processed only when needed. This means that for the majority of cases, only a small portion ("chunks") of the EPUB or PDF file is required at a time while reading.
This guide explains how to create a reading system that is capable of "streaming" EPUB and PDF files from a remote HTTP server by using HTTP range request.
This allows your users to immediately start reading EPUBs or PDFs without downloading the complete file.
Pre-requisites
The server hosting the publication must support range requests:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
If the server hosting the publications is on another domain, you also need to make sure that CORS has been configured correctly so the browser allows your webapp to perform cross-domain requests:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Implementing IRandomAccessDataSource
To be able to "stream" publication to the reading system, you need to implement the interface IRandomAccessDataSource
from colibrio-core-io-base.d.ts
.
The following example code shows how a IRandomAccessDataSource implementation could look that uses HTTP range requests:
import { IRandomAccessDataSource } from './colibrio-core-io-base';
import { HttpRequest } from './HttpRequest';
export class HttpDataSource implements IRandomAccessDataSource {
constructor(private url: string, private size: number) {
}
/**
* Fetch a chunk of data from the data source between a start and end offset.
* This method must not throw an exception in case of a non-recoverable error.
*
* @param startOffset - An index into the data source indicating the first byte to include in the result buffer
* @param endOffset - An index into the data source indicating the first byte that will *not* be included in the result buffer.
* Must be larger than 'start', and less than or equal to the value returned by getSize().
* @return Resolved with an ArrayBuffer containing the requested data. Rejected if it is not possible to fetch the requested data.
*/
fetchChunk(startOffset: number, endOffset: number): Promise<ArrayBuffer> {
// Our endOffset is *exclusive*, while range request includes the byte with the end offset.
return HttpRequest.sendRangeRequest(this.url, startOffset, endOffset - 1);
}
/**
* Get the size of the data in bytes.
*/
getSize(): number {
return this.size;
}
}
export class HttpRequest {
static sendRangeRequest(url: string, start: number, end: number): Promise<ArrayBuffer> {
return new Promise<ArrayBuffer>((resolve, reject) => {
let request = HttpRequest.createRequest('GET', url, reject);
request.setRequestHeader("Range", `bytes=${start}-${end}`);
request.onload = function(_event) {
resolve(this.response);
};
request.send();
});
}
private static createRequest(verb: 'GET' | 'HEAD', url: string, rejectFn: (arg?: any) => void): XMLHttpRequest {
let request = new XMLHttpRequest();
request.open(verb, url , true);
request.responseType = "arraybuffer";
request.onerror = function(_event) {
rejectFn(this.status + ': ' + this.statusText);
};
request.onabort = function(_event) {
rejectFn(this.status + ': ' + this.statusText);
};
request.ontimeout = function(_event) {
rejectFn('TIMEOUT');
};
return request;
}
}
Loading the publication using IRandomAccessDataSource
Using the HttpDataSource above, the Colibrio Reader Framework can now stream EPUB publications over HTTP:
import {OcfResourceProvider} from './colibrio-core-publication-epub';
let remoteEpub = new HttpDataSource('https://my-domain-with-publications/someEpub.epub', 43134054);
let ocfResourceProvider = await EpubOcfResourceProvider.createFromRandomAccessDataSource(remoteEpub);
let publication = ocfResourceProvider.getDefaultPublication();
// ... and then continue setup the reading system as usual.
The colibrio framework can stream PDF publications over HTTP with:
import {PdfPublication} from './colibrio-core-publication-pdf';
let remotePdf = new HttpDataSource('https://my-domain-with-publications/somePdf.pdf', 38284889);
let publication = PdfPublication.createFromRandomAccessDataSource(remotePdf);
// ... and then continue setup the reading system as usual.