The breakpoint of the programming of the IOS Development network continues to pass _ios

Source: Internet
Author: User
Tags md5 hash

Objective

Network download is the function we often use in the project, if it is a small file download, such as pictures and text, we can directly request the source address, and then download the complete. But if you are downloading large audio and video files, it is not possible to download it once, the user may download a period of time, close the program, go home and then download. At this time, you need to implement the function of breakpoint continuation. Allow users to pause the download at any time, the next time to start downloading, but also the last download progress.

Today we'll look at how we can simply encapsulate a breakpoint continuation class to achieve the following functions.

1. The user only needs to call an interface to download, and can get the progress of the downloads.

2. Download successful, can get the location of file storage

3. Download failed, give reasons for failure

4. You can pause the download, the next time you start downloading, and then continue to download the last progress

Principle explanation

To achieve the function of the continuation of the breakpoint, it is usually necessary for the client to record the current download progress and notify the service side of the content fragment that needs to be downloaded when it needs to be continued.

In the HTTP1.1 Protocol (RFC2616) defines the continuation of a breakpoint related HTTP headers Range and Content-Range fields, one of the simplest extension implementations of breakpoints is probably as follows:

1. The client downloads a 1024K file and has downloaded 512K

2. Network interruption, client request renewal, so you need to declare in the HTTP header this need to continue the fragment: Range:bytes=512000- this header notifies the server to start transferring files from the 512K location of the file
3. The server receives a breakpoint renewal request, starts transmission from the 512K location of the file, and increases in the HTTP header: Content-Range:bytes 512000-/1024000 and the HTTP status code returned by the server at this time should be 206 instead of 200.

Difficult notes

1. How the client obtains the number of bytes of files that have been downloaded

Client side, we need to record the size of each user download each time, and then implement the principle of step 1 of the function.

So how to record it?

In fact, we can directly get the size of the file under the specified path, iOS has provided the relevant functions, the implementation code as follows,

[[[Nsfilemanager Defaultmanager] Attributesofitematpath:filestorepath error:nil][nsfilesize] IntegerValue]

2. How to get the total bytes of downloaded files

In the previous step, we got the number of bytes of the downloaded file, we need to get the total number of bytes downloaded, and with these two values, we can work out the download progress.

So how do we get it? Here we need to use the HTTP header conten-length field, first to see the meaning of the field

Content-LengthUsed to describe the transmission length of an HTTP message entity the transfer-length of the message-body . In the HTTP protocol, the message entity length differs from the message entity's transmission length, for example, under gzip compression, the message entity length is the length before compression, and the message entity's transmission length is the length of the gzip compression.

To put it simply, the content-length number of bytes that represents the downloaded file.

In the third step of the principle of contrast, we can see that if you want to calculate the total number of bytes in the file, you must add the number of bytes that have already been downloaded content-length .

We need to store the total number of bytes per downloaded file, where we choose to use the Plist file to record that the plist file contains a dictionary. Sets the file name to the key value, and the number of file bytes already downloaded is a value.

File name to prevent duplication, here we set the file name of the download URL hash value, can be guaranteed not heavy.

The implementation code is as follows:

-(void) Urlsession: (Nsurlsession *) session Datatask: (Nsurlsessiondatatask *) Datatask Didreceiveresponse: ( Nshttpurlresponse *) Response Completionhandler: (void (^) (nsurlsessionresponsedisposition)) Completionhandler
{
  self.totallength = [response.allheaderfields[@ "Content-length"] integervalue] + downloadlength;

  Nsmutabledictionary *dict = [Nsmutabledictionary dictionarywithcontentsoffile:totallengthplist];

  if (dict = = nil) Dict = [Nsmutabledictionary dictionary];
  dict[Filename] = @ (self.totallength);

  [Dict writetofile:totallengthplist atomically:yes];
}

The above NSSessionDelegate method is called once when the request receives a response, and we can get the response information in the method and take out the content-length field.

3. Package a method to achieve download progress, success, failure tips

We can imitate AFNetwork , package the download to a method, and then use different block to achieve the download progress, success, after the failure of the callback.

The definition is as follows:

-(void) Downloadwithurl: (NSString *) URL
       progress: (Progressblock) Progressblock
        success: (Successblock) Successblock
         Faile: (faileblock) faileblock
{
  self.successblock = Successblock;
  Self.failedblock = Faileblock;
  Self.progressblock = Progressblock;
  Self.downloadurl = URL;
  [Self.task resume];
}

The above three block all adopt the macro definition way, this looks relatively concise, the concrete code refers to the following complete code.

We can then NSURLSessionDataDelegate implement three calls in the corresponding proxy method block , and then pass in the corresponding arguments. This way, when someone else calls our method, the callback can be implemented in the appropriate block . The code refers to the complete code below

Full Code implementation

Here is the complete code implementation

 #import <Foundation/Foundation.h> typedef void (^successblock) (NSString *
Filestorepath);
typedef void (^faileblock) (Nserror *error);

typedef void (^progressblock) (float progress);
@interface downloadmanager:nsobject <NSURLSessionDataDelegate> @property (copy) Successblock Successblock;
@property (copy) Faileblock Failedblock;


@property (copy) Progressblock Progressblock; -(void) Downloadwithurl: (NSString *) URL progress: (Progressblock) Progressblock success: (Successblock) SUCCESSB

Lock Faile: (Faileblock) Faileblock;
+ (instancetype) sharedinstance;

-(void) stoptask; @end 
#import "DownLoadManager.h" #import "nsstring+hash.h" @interface downloadmanager ()/** Download Task * * @property (nonatomic, St
Rong) Nsurlsessiondatatask *task;
/** Session * * @property (nonatomic, strong) nsurlsession *session;
/** writes the stream object of the file * * * @property (nonatomic, strong) Nsoutputstream *stream;
The total size of the/** file * * * @property (nonatomic, assign) Nsinteger totallength;

@property (Nonatomic,strong) NSString *downloadurl; The @end//file name (the file name in the sandbox) is generated using the MD5 hash URL, which guarantees that the filename is unique #define FILENAME self.downLoadUrl.md5String//File storage path (caches) #define F Ilestorepath [[Nssearchpathfordirectoriesindomains (Nscachesdirectory, Nsuserdomainmask, YES) LastObject] Stringbyappendingpathcomponent:filename]//Use plist file to store downloaded file size #define Totallengthplist [ Nssearchpathfordirectoriesindomains (Nscachesdirectory, Nsuserdomainmask, YES) Lastobject] stringbyappendingpathcomponent:@ "Totallength.plist"]//file has been downloaded size #define DOWNLOADLENGTH [[[Nsfilemanager Defaultmanager] Attributesofitematpath:filestorepath Error:nil][nsfilesiZe] integervalue] @implementation Downloadmanager #pragma mark-Create a single instance static ID _instance;
  + (Instancetype) Allocwithzone: (struct _nszone *) zone {static dispatch_once_t oncetoken;
  Dispatch_once (&oncetoken, ^{_instance = [Super Allocwithzone:zone];
  });
return _instance;
  } + (Instancetype) sharedinstance {static dispatch_once_t oncetoken;
  Dispatch_once (&oncetoken, ^{_instance = [[Self alloc] init];
  });
return _instance;

}-(ID) Copywithzone: (Nszone *) zone {return _instance;}

-(ID) Mutablecopywithzone: (Nszone *) zone {return _instance;} #pragma mark-Public Method-(void) Downloadwithurl: (NSString *) URL progress: (Progressblock) Progressblock success: (
  Successblock) Successblock Faile: (faileblock) faileblock {self.successblock = Successblock;
  Self.failedblock = Faileblock;
  Self.progressblock = Progressblock;
  Self.downloadurl = URL;


[Self.task resume];

}-(void) stoptask{[self.task suspend]; } #pragma mark-getter Method-(Nsurlsession *) session {if (!_session) {_session = [nsurlsession Sessionwithconfiguration:[nsurlsessionco
  Nfiguration Defaultsessionconfiguration] delegate:self delegatequeue:[[nsoperationqueue alloc] init];
return _session; }-(Nsoutputstream *) stream {if (!_stream) {_stream = [Nsoutputstream Outputstreamtofileatpath:filestorepath AP
  Pend:yes];
return _stream; }-(Nsurlsessiondatatask *) Task {if (!_task) {Nsinteger totallength = [[Nsdictionary dictionarywithcontentsoffi

    le:totallengthplist][Filename] IntegerValue];
      if (totallength && downloadlength = = totallength) {NSLog (@ "##### #文件已经下载过了");
    return nil; }//Create request Nsmutableurlrequest *request = [Nsmutableurlrequest requestwithurl:[nsurl URLWithString:self.downLoadU

    RL]]; Set the request header//range:bytes=xxx-xxx, download the nsstring *range = [NSString stringwithformat:@] from the length of the download to the end of the total length of the file Bytes=%zd
    -", downloadlength]; [Request Setvalue:range Forhttpheaderfield:@ "Range"];
  Create a data task _task = [Self.session datataskwithrequest:request];
return _task; #pragma mark-<NSURLSessionDataDelegate>/** * 1. Received response * *-(void) Urlsession: (Nsurlsession *) session Datatask: (Nsurlsessiondatatask *) datatask didreceiveresponse: (nshttpurlresponse *) response Completionhandler: (void (^) (

  nsurlsessionresponsedisposition)) Completionhandler {//Open the stream [Self.stream open]; /* (The Content-length field returns the server for each client request to download the file size) such as first client request download file A, size 1000byte, then the first server returned content-length = 1000, the client download to 5 00byte, suddenly interrupted, again requested range is "bytes=500-", then the server returned Content-length 500 so for a single file for multiple downloads (breakpoint continued), calculate the total size of the file, Content-length must be returned by the server plus locally stored downloaded file size * * self.totallength = [response.allheaderfields[@ "Content-length"]

  IntegerValue] + downloadlength; Store the downloaded file size in the plist file nsmutabledictionary *dict = [Nsmutabledictionary dictionarywithcontentsoffile:
  Totallengthplist];
  if (dict = = nil) Dict = [Nsmutabledictionary dictionary]; Dict[Filename] = @ (self.totallength);

  [Dict writetofile:totallengthplist Atomically:yes];
Receives this request, allows the receiving server the data Completionhandler (Nsurlsessionresponseallow); /** * 2. Receive the data returned by the server (this method may be called n times)/-(void) Urlsession: (Nsurlsession *) session Datatask: (Nsurlsessiondatatask *) data

  Task didreceivedata: (NSData *) data {//write to [Self.stream write:data.bytes maxLength:data.length];
  Float Progress = 1.0 * DOWNLOADLENGTH/SELF.TOTALLENGTH;
  if (self.progressblock) {self.progressblock (progress); }//Download Progress}/** * 3. Completion of request (success/Failure)/-(void) Urlsession: (Nsurlsession *) session Task: (Nsurlsessiontask *) Task Didcomple
    Tewitherror: (Nserror *) Error {if (error) {if (Self.failedblock) {self.failedblock (error);
    } Self.stream = nil;

  Self.task = nil;
    }else{if (self.successblock) {self.successblock (Filestorepath);
    }//Close stream [Self.stream closed];
    Self.stream = nil;
  Clear task Self.task = nil; }} @end

How to call

@interface Viewcontroller ()
@end

@implementation viewcontroller
/**
 * started downloading * *
(ibaction) Start: (ID) Sender {
  //start Task
  NSString * DownloadURL = @ "http://audio.xmcdn.com/group11/M01/93/AF/ WKGDA1DZZJLBL0GCAPUZEJQK84Y539.M4A ";

  [[Downloadmanager Sharedinstance]downloadwithurl:downloadurl progress:^ (float progress) {
    NSLog (@ "###%f", Progress);

  } success:^ (NSString *filestorepath) {
    NSLog (@ "###%@", Filestorepath);

  } faile:^ (Nserror *error) {
    NSLog (@ "###%@", Error.userinfo[nslocalizeddescriptionkey]);}
/**
 * Suspend Download * *
(ibaction) pause: (ID) Sender {[
  downloadmanager sharedinstance]stoptask];
}

@end

Summarize

Here can only achieve a single task to download, we can think of ways to do, see how to achieve multitasking download, and realize the function of breakpoint extension. And in order to make it easier to operate, it is recommended to replace storage information with database storage. The above is the entire content of this article, I hope to learn about the development of iOS help.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.