Objective
Summary of Breakpoint Continuation
The continuation of a breakpoint is the downloading or uploading of data from the place where the file was last interrupted, rather than starting with the file. (The text of the breakpoint is only related to download, upload is not discussed) when downloading large files, if you do not implement a breakpoint to continue the function, then each occurrence of abnormal or user active pause, will go to download, this is a waste of time. So in the project to achieve large file downloads, the breakpoint continues to pass the function is essential. Of course, there is a special situation in the continuation of the breakpoint, that is, the IOS application is killed by the user or the application of crash, to implement the application restart after the breakpoint continued transmission. This special situation is the problem to be solved in this paper.
The principle of continuous transmission of breakpoint
To implement a continuation of a breakpoint, the server must support it. Currently the most common are two ways:FTP and HTTP.
The following is a brief introduction to the principle of HTTP breakpoint continuation.
HTTP
Through HTTP, can be very convenient to achieve the continuation of the breakpoint. The continuation of a breakpoint is mainly dependent on the Range of the HTTP header definition. When requesting a range of resources, you can more efficiently make requests to large resources or recover downloads from transmission errors. With Range, the application can recover the download of a resource by using an HTTP request to retrieve a return or part of a failed resource. Of course not all servers support Range, but most servers are available. Range is calculated in bytes, and the request does not have to give a trailing byte number because the requester does not necessarily know the size of the resource.
The Range is defined as shown in Figure 1:
Figure 1. Http-range
Figure 2 shows the header information for HTTP request:
Figure 2. HTTP Request Example
In the example above, "range:bytes=1208765-" represents the portion of the requested resource after the beginning of 1208765 bytes.
Figure 3 shows the header information for the HTTP response:
Figure 3. HTTP Response Example
The "Accept-ranges:bytes" in the example above represents a range of server-side accepted request resources and allows byte-type access to the specified resource. " Content-Range: bytes 1208765-20489997/20489998
" indicates that the return provides the location within the original entity where the requested resource resides, and gives the length of the entire resource. The point to note here is that HTTP return code is 206 rather than 200.
Analysis of-afhttprequestoperation of breakpoint continuous transmission
After understanding the principle of the continuation of the break point, we can start to implement a breakpoint in the IOS application. Because the author project resources are deployed on the HTTP server, so the breakpoint continues to pass the function is based on HTTP implementation. First look at the implementation provided in the afnetworking of the third-party network framework. The Listing 1 sample code is the code used to implement the part of the breakpoint continuation:
Listing 1. Code to implement breakpoint continuation using Afhttprequestoperation
1 Specify download file address urlstring//2 Get saved file path FilePath//3 Create nsurlrequest nsurlrequest *request = [Nsurlrequest REQUESTW
Ithurl:[nsurl Urlwithstring:urlstring]];
unsigned long long downloadedbytes = 0; if ([[[[[Nsfilemanager Defaultmanager] Fileexistsatpath:filepath]) {//3.1 if previously downloaded, add Range///Get SI for downloaded file in HTTP request header
Ze downloadedbytes = [self filesizeforpath:filepath]; Verify that the file is downloaded if (downloadedbytes > 0) {//if downloaded, the Range nsmutableurlrequest of the HTTP header part is modified when the breakpoint is renewed *mutableur
Lrequest = [request mutablecopy];
NSString *requestrange = [NSString stringwithformat:@ "bytes=%llu-", downloadedbytes];
[Mutableurlrequest setvalue:requestrange forhttpheaderfield:@ "Range"];
request = Mutableurlrequest; }//4 create afhttprequestoperation afhttprequestoperation *operation = [[Afhttprequestoperation alloc] InitWithRe
Quest:request]; 5 Set the operation output stream, save in the 2nd step of the file Operation.outputstream = [Nsoutputstream Outputstreamtofileatpath:filepaTh append:yes]; 6 Setting download progress processing block [Operation setdownloadprogressblock:^ (Nsuinteger bytesread, Long long totalbytesread, long long to Talbytesexpectedtoread) {///Bytesread bytes currently read//totalbytesread read total bytes, including//totalbytesexpectedtoread file size before breakpoint continuation
Small}]; 7 Set success and failure processing block [Operation setcompletionblockwithsuccess:^ (Afhttprequestoperation *operation, id re
Sponseobject) {} failure:^ (afhttprequestoperation *operation, Nserror *error) {}]; 8 Start operation [Operation Start];
Using the above code, the extension of the breakpoint function is implemented, application restart or abnormal circumstances, can be based on the part of the download to start downloading. The key place is to persist the data that has already been downloaded. The next step is to see how afhttprequestoperation is implemented. By looking at the source, we found afhttprequestoperation inherited from afurlconnectionoperation , and afurlconnectionoperation implemented the nsurlconnectiondatadelegate protocol.
The processing process is shown in Figure 4:
Figure 4. Afurlhttprequestoperation processing Process
Here afnetworking why the threading asynchronous interface is adopted because the asynchronous interface is invoked directly on the main thread, and there is a runloop problem. When the main thread is called [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]
, the Listener task after the request is sent is added to the runloop of the main thread,runloopmode defaults to Nsdefaultrunloopmode, which indicates that only when the runloop of the frontline process N Sdefaultrunloopmode , this task will be executed. And when the user scrolling tableview and ScrollView, the main thread of the Runloop in nseventtrackingrunloop mode, it will not execute nsdefaultrunloopmode< /c10> 's mission.
In addition, the downloadprogressblock,success and failure blocks on this side need to be returned to the main thread because of the way the child threads invoke the interface.
The breakpoint continues to pass the actual combat
After you understand the principles and afhttprequestoperation examples, look at the three ways to achieve a continuation of a breakpoint:
Nsurlconnection
Based on Nsurlconnection, the key is to satisfy the Nsurlconnectiondatadelegate protocol, the following three methods are mainly realized:
Listing 2. The realization of nsurlconnection
SWIFT//Request Failure processing func connection (connection:nsurlconnection, Didfailwitherror error:nserror) {Self.failureh Andler (Error:error)}//Received server response is called Func connection (connection:nsurlconnection, Didreceiveresponse Response:ns Urlresponse) {if self.totallength!= 0 {return} Self.writehandle = Nsfilehandle (forwritingatpath:fil
EManager.instance.cacheFilePath (self.filename!)) Self.totallength = Response.expectedcontentlength + self.currentlength}//When the server returns entity data is called Func connection (connection : Nsurlconnection, Didreceivedata data:nsdata) {Let length = data.length//move to the end of file Self.write Handle.seektoendoffile ()//write data to Sanbox self.writeHandle.writeData (data)//Calculate data length s Elf.currentlength = self.currentlength + length print ("Currentlength\ (self.currentlength)-totallength\" ( Self.totallength) ") if (Self.downloadprogresshandler!= nil) {self.downloadprogresshandler:Length, TotalBytes:self.currentLength, TotalBytesExpected:self.totalLength)}///call func after download connection
Didfinishloading (connection:nsurlconnection) {self.currentlength = 0 self.totallength = 0//close Write handle Self.writeHandle.closeFile () Self.writehandle = nil Let Cachefilepath = FileManager.instance.cacheFilePath (self
. filename!)
Let Documenfilepath = FileManager.instance.documentFilePath (self.filename!)
Do {try FileManager.instance.moveItemAtPath (Cachefilepath, Topath:documenfilepath)} catch let E as Nserror {
Print ("Error occurred when to move file: \ (E)")} Self.successhandler (responseobject:filename!) }
As shown in Figure 5, the general processing flow of the nsurlconnection is illustrated.
Figure 5. Nsurlconnection process
According to the general process in Figure 5, initialize Filehandler in didreceiveresponse , in didreceivedata , the file that will be received in the Connectio In Ndidfinishloading , empty the data and close the Filehandler, and save the file to the document directory. So when an exception is requested or the application is killed by the user, it is possible to continue the breakpoint by persisting the intermediate file. When initializing nsurlconnection, be aware that setting Scheduleinrunloop is Nsrunloopcommonmodes, or the progress bar UI cannot be updated.
The implementation effect is shown in Figure 6:
Figure 6. Nsurlconnection Demo
Nsurlsessiondatatask
Apple started in iOS7 with a new class nsurlsession, which has the nsurlconnection method and is more powerful. Due to the abandonment of the nsurlconnection from 2015 onwards, the reader recommends a continuation based on Nsurlsession. The mapping relationship between the nsurlconnection and Nsurlsession delegate methods, as shown in Figure 7. So the key is to meet Nsurlsessiondatadelegate and nsurlsessiontaskdelegate.
Figure 7. Mapping relationships between protocols
The code, as shown in Listing 3, is basically the same as the nsurlconnection implementation.
Listing 3. The realization of Nsurlsessiondatatask
SWIFT
//Receive Data
func urlsession (session:nsurlsession, Datatask:nsurlsessiondatatask,
idreceivedata Data:nsdata) {
//...
}
Receive server Response
func urlsession (session:nsurlsession, Datatask:nsurlsessiondatatask,
didreceiveresponse Response:nsurlresponse, Completionhandler:
(nsurlsessionresponsedisposition)-> Void) {
//...
Completionhandler (. Allow)
}
//Request Complete
func urlsession (session:nsurlsession, Task:nsurlsessiontask,
Didcompletewitherror error:nserror?) {
if error = = nil {
//...
Self.successhandler (responseobject:self.filename!)
} else {
Self.failurehandler (error:error!)
}
}
The difference in with Didcomletewitherror, it will be nsurlconnection in the connection:didfailwitherror:
and connectiondidfinishloading: integrated together, so this side should be based on error to distinguish between the execution of successful block and failed block.
The implementation effect is shown in Figure 8:
Figure 8. Nsurlsessiondatatask Demo
Nsurlsessiondowntask
Finally, look at the class Nsurlsessiondownloadtaskfor downloading in nsurlsession, the corresponding protocol is nsurlsessiondownloaddelegate, as shown in Figure 9:
Figure 9. Nsurlsessiondownloaddelegate protocol
After exiting Didfinishdownloadingtourl , the corresponding files in the temp directory are automatically deleted. So the file operation must be handled within this method. Before I wanted to find this TMP file, based on this file to do a continuation of the breakpoint, but has been unable to find the path to this file. After SWIFT announces Nsurlsession's source code, there may be a way to find it. Based on Nsurlsessiondownloadtask, you need to save the downloaded data in cancelbyproducingresumedata . Progress notice is very simple, directly in the URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:
implementation can be.
The code looks like listing 4:
Listing 4. The realization of Nsurlsessiondownloadtask
SWIFT//ui triggers pause func pause () {Self.downloadtask? Cancelbyproducingresumedata ({data-> Void in if data!= nil {data!. WriteToFile (FileManager.instance.cacheFilePath (self.filename!), Atomically:false)}}) Self.downloadtask = Ni L//MARK:-nsurlsessiondownloaddelegate func urlsession (session:nsurlsession, Downloadtask:nsurlsessiondown Loadtask, Didwritedata Byteswritten:int64, Totalbyteswritten:int64, Totalbytesexpectedtowrite:int64) {if (self.do Wnloadprogresshandler!= Nil) {Self.downloadprogresshandler (Bytes:int (byteswritten), TOTALBYTES:TOTALBYTESWR Itten, Totalbytesexpected:totalbytesexpectedtowrite)}} func urlsession (Session:nsurlsession, Task:nsurlsessio Ntask, Didcompletewitherror error:nserror?
{if error!= nil {//real error self.failurehandler (error:error!) } func urlsession (Session:nsurlsession, Downloadtask:nsurlsessiondownloadtask, Didfinishdownloadingtourl Locati ON:nsurl) {Let Cachefilepath = FileManager.instance.cacheFilePath (self.filename!)
Let Documenfilepath = FileManager.instance.documentFilePath (self.filename!) Do {if FileManager.instance.fileExistsAtPath (cachefilepath) {try FileManager.instance.removeItemAtPath (Cachef Ilepath)} Try FileManager.instance.moveItemAtPath (location.path!, Topath:documenfilepath)} catch let E as
Nserror {print ("Error occurred when to move file: \ (E)")} Self.successhandler (Responseobject:documenfilepath) }
The implementation effect is shown in Figure 10:
Figure 10. Nsurlsessiondownloadtask Demo
Summarize
The above is the summary of the development of iOS in the end of the continuation of the breakpoint and practice of all content, in fact, the implementation of the download far more than these content, this article only introduced a simple use. Hope that in further study and application can continue to share with you. I hope this article can help everyone in need.