Q: My app downloads large files over HTTP. How can I resume a partially completed download?
A: The answer depends on your target platform:
If you're working on Mac OS X it's easy to resume downloads using the NSURLDownload class. See the URL Loading System Programming Guide for more information about this.
NSURLDownload is not supported on iOS. However, if you're a Newsstand app, Newsstand has built-in support for resuming downloads. See Newsstand Kit Framework Reference for the details.
If you're a generic iOS app, you will have to write the code to resume downloads yourself, as explained below.
Resuming an HTTP download isn't too difficult, but you need to understand some critical HTTP concepts:
entity tag — This is a unique identifier provided by the server that denotes a specific version of a specific resource; if someone changes the resource on the server, the entity tag will change.
Range header — This allows you to request a specific range of bytes from a resource.
If-Range header — This header specifies that you only want to get a range of bytes from a resource if the entity tag hasn't changed.
The basic strategy for resuming a download is:
When you do the initial download, save the entity tag associated the resource.
As you save data to disk, remember how much of the data is valid.
When you come to resume the download, get the entity tag and the amount of data you've saved and apply these values to the request via the
Execute the request. It will either succeed (and you'll receive the remaining bytes of the resource) or it will fail (in which case you have to get the entire resource from scratch).
If the server doesn't support entity tags you can do similar things with the last-modified date.
For a concrete example of this, you can use a packet trace (see Technical Q&A QA1176, 'Getting a Packet Trace') to look at how Safari resumes a download on your Mac. Listing 1 shows a typical resume request.
Listing 1 An HTTP resume request
GET /download.info.apple.com/[...]/MacOSXUpdCombo10.6.8.dmg HTTP/1.1
User-Agent: Safari/7534.52.7 [...]
Accept-Encoding: gzip, deflate
Range header tells the server you want to get the data starting at offset 4041400. The
If-Range header tells the server you only want that data if the data hasn't changed since the server gave the client the supplied entity tag (that is, "968f3f3e86e0339ce722170ae656bc73:1319461845").
Listing 2 shows the corresponding response.
Listing 2 An HTTP resume response
HTTP/1.1 206 Partial Content
Last-Modified: Mon, 24 Oct 2011 13:04:42 GMT
Date: Mon, 23 Jan 2012 16:13:25 GMT
Content-Range: bytes 4041400-1087036999/1087037000
The HTTP status (206) tells you that the response only contains a subset of the resource. The
Content-Range header tells you exactly what range of the resource the server is returning (byte 4041400 through to byte 1087036999) and total length of the resource (1087037000). Finally, the
Content-Length header tells you how many bytes the server is returning in this particular response.
Document Revision History
New document that explains how to resume HTTP downloads.