Implementing Download Resuming
This document describes how an embedder or other Gecko/Necko-using application can implement download resuming.
The interfaces in question exist in the form they are described here since Gecko 1.8a4 (Firefox 1.5, SeaMonkey 1.0, XULRunner 18.104.22.168).
Various protocols support getting partial files. This means that if a download was interrupted, it can be resumed from that point on, rather than regetting the whole file. The Necko implementations of HTTP 1.1 (RFC 2616) as well as FTP support this feature.
Not only is the ability to specify a start position important, but it's also important to have some assurance that the file did not change since the initial download attempt. That would lead to a situation where the first part of the file corresponds to the initial version, while the latter part belongs to a different version, leading to an unusable result.
Resuming is done using the
nsIResumableChannel interface. The expected way to use it is this:
- For all downloads that happen, get the
entityIDfrom the channel, and store it for later use. The entity ID is what ensures that the file remains unchanged. This can also be used to check whether the download is resumable: if it is not (e.g. the server is not using HTTP 1.1), then accessing this attribute will throw an
- If the download gets interrupted, Necko will call the stream listener's
onStopRequestmethod with a failure status. (TODO: document what webbrowserpersist/exthandler do). The front-end code can then notify the user of this, and offer to resume the download.
In order to resume a download, you should create a channel as usual (using
nsIIOService). Then, you can check if it implements
nsIResumableChannel. If it does not, the protocol does not support resuming. Otherwise, call
resumeAt with the desired start position, and the previously stored entity ID. (It is possible to pass an empty string as the ID; however, this means that you have no assurance that the file remained unchanged)
Now, you can open the channel as usual (using
nsIChannel.asyncOpen() in the common case) and write to the file in the
onDataAvailable notifications. You may want to use
nsISimpleStreamListener to simplify this task; to get progress notifications, you can implement
nsIProgressEventSink and set an interface requester as the
notificationCallbacks of the channel that gives out such an event sink (this needs to be done before calling
If the file changed (that is, the entity ID does not match), then Necko will notify the stream listener with an
NS_ERROR_ENTITY_CHANGED error code. The front-end may want to offer the user to get the new file from scratch in such a case.
In some cases, it's not possible to detect whether the server supports resuming until the user actually tries to resume. In such a case, the stream listener will get an
NS_ERROR_NOT_RESUMABLE status. Here, too, the front-end may want to offer downloading the entire file from scratch.