Development of iOS The Idea of Large File Download and Breakpoint Download

  • 2021-10-11 19:46:33
  • OfStack

Large file download

Scenario 1: Use NSURLConnection and its proxy method, and NSFileHandle (not recommended after iOS9)

Related variables:


 @property (nonatomic,strong) NSFileHandle *writeHandle;
@property (nonatomic,assign) long long totalLength; 

1 > Send a request


//  Create 1 Request 
  NSURL *url = [NSURL URLWithString:@""];
  NSURLRequest *request = [NSURLRequest requestWithURL:url];
  //  Use NSURLConnection Initiate 1 Asynchronous requests 
  [NSURLConnection connectionWithRequest:request delegate:self]; 

2 > Processing the data returned by the server in the proxy method


/**  The following proxy method is called when a response is received from the server 
  1. Create 1 Empty files 
  2. Use 1 Handle objects are associated with this empty file to facilitate writing data after the empty file 
*/
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response
{
  //  Create a file path 
  NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
  NSString *filePath = [caches stringByAppendingPathComponent:@"videos.zip"];
  
  //  Create 1 Empty files to sandbox 
  NSFileManager *mgr = [NSFileManager defaultManager];
  [mgr createFileAtPath:filePath contents:nil attributes:nil];
  
  //  Create 1 File handles used to write data 
  self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
  
  //  Get the total size of the file 
  self.totalLength = response.expectedContentLength;
}

/**  The following proxy method is called when the file data returned by the server is received 
   Append data to the back of a file using a handle object 
 */
- (void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data
{
  //  Move to the back of the file 
  [self.writeHandle seekToEndOfFile];
  
  //  Write data to the sandbox 
  [self.writeHandle writeData:data];
}

/**
   Close the handle object when all data is received 
 */
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
  //  Close the file and empty it 
  [self.writeHandle closeFile];
  self.writeHandle = nil;
} 

Scenario 2: NSURLSessionDownloadTask and NSFileManager using NSURLSession


NSURLSession *session = [NSURLSession sharedSession];
  NSURL *url = [NSURL URLWithString:@""];
  //  It can be used to download large files, and the data will be stored in the sandbox tmp Folder 
  NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    // location  : The path where temporary files are stored (downloaded files) 
    
    //  Create a storage file path 
    NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
    // response.suggestedFilename The recommended file name, 1 Like the file name on the server side 1 To 
    NSString *file = [caches stringByAppendingPathComponent:response.suggestedFilename];
    
    /** Cut or copy temporary files to Caches Folder 
     AtPath : File path before cutting 
     toPath : Cut file path 
     */
    NSFileManager *mgr = [NSFileManager defaultManager];
    [mgr moveItemAtPath:location.path toPath:file error:nil];
  }];
  [task resume]; 

Scenario 3: Proxy method using NSURLSessionDownloadDelegate and NSFileManger


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  //  Create 1 Download tasks and set up agents 
  NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration];
  NSURLSession *session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]];
  
  NSURL *url = [NSURL URLWithString:@""];
  NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url];
  [task resume];
}

#pragma mark - 
/**
   Called after downloading 
   Parameters: lication  Path to temporary file (downloaded file) 
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location{
  //  Create a storage file path 
  NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
  // response.suggestedFilename The recommended file name, 1 Like the file name on the server side 1 To 
  NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
  
  /** Cut or copy temporary files to Caches Folder 
   AtPath : File path before cutting 
   toPath : Cut file path 
   */
  NSFileManager *mgr = [NSFileManager defaultManager];
  [mgr moveItemAtPath:location.path toPath:file error:nil];
}

/**
   Whenever the download is finished, 1 Part of the time (it may be called multiple times) 
   Parameters: 
    bytesWritten  How much was downloaded for this call 
    totalBytesWritten  How many lengths have you accumulated into the sandbox 
    totalBytesExpectedToWrite  Total file size 
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
   didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
  //  You can do some operations such as displaying progress here 
}

/**
   Use when resuming downloads 
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
  //  Used for breakpoint transmission 
} 

Breakpoint download

Option 1:

1 > Add two variables and buttons on the basis of scenario 1


@property (nonatomic,assign) long long currentLength;
@property (nonatomic,strong) NSURLConnection *conn; 

2 > Add the following code to the proxy method that receives the data returned by the server


  //  Record breakpoints and accumulate file length 
  self.currentLength += data.length; 

3 > Click the button to start (continue) or pause the download


- (IBAction)download:(UIButton *)sender {
  
  sender.selected = !sender.isSelected;
  
  if (sender.selected) { //  Continue (start) downloading 
    NSURL *url = [NSURL URLWithString:@""];
    // **** The key point is to use NSMutableURLRequest, Set the request header Range
    NSMutableURLRequest *mRequest = [NSMutableURLRequest requestWithURL:url];
    
    NSString *range = [NSString stringWithFormat:@"bytes=%lld-",self.currentLength];
    [mRequest setValue:range forHTTPHeaderField:@"Range"];
    
    //  Download 
    self.conn = [NSURLConnection connectionWithRequest:mRequest delegate:self];
  }else{
    [self.conn cancel];
    self.conn = nil;
  }
} 

4 > Add the following code to line 1 of the proxy method that receives server response execution to prevent duplicate creation of empty files


 if (self.currentLength) return; 

Scenario 2: Proxy method using NSURLSessionDownloadDelegate

Required variables


 @property (nonatomic,strong) NSURLSession *session;
@property (nonatomic,strong) NSData *resumeData; // Contains the starting location for continuing the download and the download url
@property (nonatomic,strong) NSURLSessionDownloadTask *task; 

Method


//  Create 1 Request 
  NSURL *url = [NSURL URLWithString:@""];
  NSURLRequest *request = [NSURLRequest requestWithURL:url];
  //  Use NSURLConnection Initiate 1 Asynchronous requests 
  [NSURLConnection connectionWithRequest:request delegate:self]; 
0

Related articles: