iOS小技能:文件上传和下载(断点下载、断点续传)

一起养成写作习惯!这是我参与「日新计划 4 月更文挑战」的第6天,点击查看活动详情。

引言

小文件下载:直接用NSData的+ (id)dataappetiteWithContentsOfURL:(NSURL *)url;利用NSHTTPURLConnection发送一个HTTP请求去下载逆向选择

如果是下载图片,还可以利用SDWebImage框架

I HTTP Rang逆向行驶扣几分罚款多少钱e(指定每次从网路下载数据包的大小)

通过设置请求头Range可以指定每次从网路下载数据包的大小,可以用于断点下载


 [request setValue:[NSString stringWithFormat:@"bytes=%lld-",self.currentDataLength] forHTTPHeaderField:@"Range"];

1.1 Range示例

bytes=0-499  从0到499的头500个字节
 bytes=500-999  从500到999的第二个500字节
bytes=500-  从500字节以后的所有字节
bytes=-500  最后500个字节
bytes=500-599,800-899  同时指定几个范围 

1.2 Range小结

  • 用于分隔

前面的数字表示起始字节数 后面的数组表示截止字节数,ios是苹果还是安卓没有表示到末尾 用于分组,可以一次指定多个Range,不approve过很少用

  • 第三方解压缩框架——SSZipArcappointmenthive

Adding to your project

Add SSZipArchive.h, SSZipArchive.m, and minizip to your project.
Add the libz library to your target 需要引入libz.dylib框架
  • Usage

// Unzipping
NSString *zipPath = @"path_to_your_zip_file";
NSString *destinationPath = @"path_to_the_folder_where_you_want_it_unzipped";
[SSZipArchive unzipFileAtPath:zipPath toDestination:destinationPath];
// Zipping
NSString *zippedPath = @"path_where_you_want_the_file_created";
NSArray *inputPaths = [NSArray arrayWithObjects:
                       [[NSBundle mainBundle] pathForResource:@"photo1" ofType:@"jpg"],
                       [[NSBundle mainBundle] pathForResource:@"photo2" ofType:@"jpg"]
                       nil];
[SSZipArchive createZipFileAtPath:zippedPath withFilesAtPaths:inputPaths];

1.3ios15.4正式版 NSURLConnec多线程是什么tion断点下载


  • 断点下载

#pragma mark - 断点下载
- (IBAction)pause:(UIButton *)sender {
    [sender setSelected:!sender.selected];
    if (sender.selected) {//继续下载
        NSURL *url = [NSURL URLWithString:@"http://127.0.0.1:8080/MJServer/resources/videos/iPhoneSimulator6.1.sdk.zip"];
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        //设置Range
        [request setValue:[NSString stringWithFormat:@"bytes=%lld-",self.currentDataLength] forHTTPHeaderField:@"Range"];
        NSLog(@"%@",[NSString stringWithFormat:@"bytes=%lld-",self.currentDataLength]);
        self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
    }else{//暂停下载
        [self.connection cancel];
        self.connection = nil;
    }
}
#pragma mark - NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    self.response = (NSHTTPURLResponse*)response;
    if (self.currentDataLength) {//断点之后的新请求,无需执行以下代码
        return;
    }
    //创建一个和预期下载文件一样大小的文件到沙盒--以便多线程断点下载,的线程分工;----单线程断点下载只需创建一个空文件
    NSString *filePath = HSCachePath([connection.currentRequest.URL lastPathComponent]);
    NSFileManager *fileManager = [NSFileManager defaultManager];
    [fileManager createFileAtPath:filePath contents:nil attributes:nil];
    //创建文件handle
    self.fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
    self.totalDataLength = response.expectedContentLength;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    self.currentDataLength += data.length;
//    NSDictionary *dict = [self.response allHeaderFields];
//    NSString *length = dict[@"Content-Length"];//获取预期长度
//    NSString *length = self.response.expectedContentLength;
        [self.circularProgressView setProgress:self.currentDataLength*1.0/self.totalDataLength];
    [self.fileHandle seekToEndOfFile];
    [self.fileHandle writeData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    [self.fileHandle closeFile];
    self.currentDataLength =0;
}

II 文件上传( multipart/form-data)


  • 设置Post的请求头(key valuios系统e)
/*Content-Type   multipart/form-data; boundary=本次上传标示字符串(不能中文)*/
[request setValue:@"multipart/form-data;boundary=hisun" forHTTPHeaderField:@"Content-Type"];

The MIME type of the specified dHTTPata. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types

2.1多线程并发 文件上传的请求体


iOS小技能:文件上传和下载(断点下载、断点续传)

  • 文件参数

–本次上传标示字符串(边界比请求头的boundary 增加–开头) Content-Disposition: formios15.4值得更新吗-data; name=”参httpclient数名”; filename=”文件名”
Content-Type: MIMEType (文件类型) 文件具体数据

  • 非文件参数

–本次上传标示字符串(–边界) Content-Disposition: form多线程编程-data; name=”参数名称”;
参数值 –边界–(结尾标记 )

结尾标记(–边界–)

  • zip 压缩文件夹
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
   NSString *filePath = [cachePath stringByAppendingPathComponent:@"images"];
   NSString *zipPath = [cachePath stringByAppendingPathComponent:@"images.zip"];
   NSLog(@"%@,%@",filePath,zipPath);
   [SSZipArchive createZipFileAtPath:zipPath withContentsOfDirectory:filePath];
  • 根据URL 获取mimeType
- (NSString *) mimeTypeWithUrl:(NSURL *)url{
    NSURLResponse *response;
    [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:&response error:nil];
    return response.MIMEType;
}
//    NSString *mimetype = [self mimeTypeWithUrl:[NSURL fileURLWithPath:@"/Users/devzkn/Desktop/minion_02.png"]];

2.2 使用NSMutableURLRequest文件上传


#define HSBasicUrl  @"http://127.0.0.1:8080/KNServer/"
#define HSUrl(name) [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",HSBasicUrl,name]]
#define HSFileBoundary @"hisun"
#define HSFileEndedBounary [NSString stringWithFormat:@"--%@--",HSFileBoundary]
#define HSFileStartBoundary [NSString stringWithFormat:@"--%@",HSFileBoundary]
#define HSNewLine @"rn"
#define HSStringEncoding(str) [str dataUsingEncoding:NSUTF8StringEncoding]
#pragma mark - 文件上传
- (void)upload:(NSString*)fileName mimeType:(NSString *)mimeType fileData:(NSData*)fileData params:(NSDictionary*)params{
    //设置请求体
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:HSUrl(@"upload")];
    //准备请求体数据
    NSMutableData *data =[NSMutableData data];
    /*
     请求体:
     1)文件参数
     --本次上传标示字符串(边界)
     Content-Disposition: form-data;  name="参数名"; filename="文件名"
     Content-Type: MIMEType  (文件类型)
     文件具体数据
     2)非文件参数
     --本次上传标示字符串(边界)
     Content-Disposition: form-data;  name="参数名称"
     参数值
     3)边界–(结尾标记 )
     */
    [data appendData:HSStringEncoding(HSFileStartBoundary)];//分界线
    [data appendData:HSStringEncoding(HSNewLine)];
    NSString *disposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name="%@"; filename="%@"",@"file",fileName];
    [data appendData:HSStringEncoding(disposition)];//参数名
    [data appendData:HSStringEncoding(HSNewLine)];
    NSString *mimeTypeStr = [NSString stringWithFormat:@"Content-Type:%@",mimeType];
    [data appendData:HSStringEncoding(mimeTypeStr)];
    [data appendData:HSStringEncoding(HSNewLine)];
    [data appendData:HSStringEncoding(HSNewLine)];
    [data appendData:fileData];
    [data appendData:HSStringEncoding(HSNewLine)];
    //设置非文件数据
    [params enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        [data appendData:HSStringEncoding(HSFileStartBoundary)];//分界线
        [data appendData:HSStringEncoding(HSNewLine)];
        NSString *disposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name="%@"",key];
        [data appendData:HSStringEncoding(disposition)];//参数名
        [data appendData:HSStringEncoding(HSNewLine)];
        [data appendData:HSStringEncoding(HSNewLine)];
        [data appendData:HSStringEncoding([obj description])];
        [data appendData:HSStringEncoding(HSNewLine)];
    }];
    //结尾
    [data appendData:HSStringEncoding(HSFileEndedBounary)];
    [data appendData:HSStringEncoding(HSNewLine)];
    [request setHTTPBody:data];
    [request setHTTPMethod:@"post"];
    [request setValue:[NSString stringWithFormat:@"multipart/form-data;boundary=%@",HSFileBoundary] forHTTPHeaderField:@"Content-Type"];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        NSLog(@"%@,%@",response,[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil]);
    }];
}

2.3 NSURLSession-断点续传


- (NSURLSession *)session{
    if (_session == nil) {
        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue] ];
    }
    return _session;
}
#pragma mark - 断点下载
- (IBAction)pause:(UIButton *)sender {
    [sender setSelected:!sender.selected];
    if (sender.selected) {
        if (self.task == nil) {
            if (self.resumeData) {
                [self resumeDownLoad];//继续下载
            }else{
                [self startSessionWithConfiguration];//开始下载
            }
        }
    }else{
        [self pauseDownload];//暂停
    }
}
#pragma mark - NSURLSessionDownloadDelegate 查看下载进度
- (void) startSessionWithConfiguration{
    NSLog(@"%s----start ",__func__);
    [self.circularProgressView setProgress:0];
    [self.button setSelected:YES];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:HSURL(@"resources/videos/iPhoneSimulator6.1.sdk.zip")];
    self.task = [self.session downloadTaskWithRequest:request];
    [self.task resume];//执行任务
}
- (void)resumeDownLoad{
    if (self.resumeData == nil) {
        return;
    }
    NSLog(@"%s-----继续",__func__);
    self.task = [self.session downloadTaskWithResumeData:self.resumeData];
    [self.task resume];
    self.resumeData = nil;
}
- (void)pauseDownload {
    NSLog(@"%s------   暂停",__func__);
    if (self.task == nil) {
        return;
    }
    /*
     A completion handler that is called when the download has been successfully canceled.
     If the download is resumable, the completion handler is provided with a resumeData object. Your app can later pass this object to a session’s downloadTaskWithResumeData: or downloadTaskWithResumeData:completionHandler: method to create a new task that resumes the download where it left off.即包含了创建链接的URL和连接的头信息(Range)
     */
    HSweakself(weakself);
    [self.task  cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
        weakself.resumeData = resumeData;
        weakself.task = nil;
    }];
}
/* Sent when a download task that has completed a download.  The delegate should
 * copy or move the file at the given location to a new location as it will be
 * removed when the delegate message returns. URLSession:task:didCompleteWithError: will
 * still be called.
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location{
    NSString *filePath = HSCachePath(downloadTask.response.suggestedFilename);
    NSLog(@"%s----%@ --%@ --%@",__func__,downloadTask.response,filePath,location.path);
    //移动location文件到filePath
    NSFileManager *fileManager = [NSFileManager defaultManager];
    [fileManager moveItemAtPath:location.path toPath:filePath error:nil];
}
/* Sent periodically to notify the delegate of download progress. */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    NSLog(@"%s----%@,bytesWritten:%lld,------totalBytesWritten:%lld,------totalBytesExpectedToWrite:%lld------%f%%",__func__,downloadTask,bytesWritten,totalBytesWritten,totalBytesExpectedToWrite,(double)totalBytesWritten*100.0/totalBytesExpectedToWrite);
    [self.circularProgressView setProgress:(double)totalBytesWritten*1.0/totalBytesExpectedToWrite];
}
/* Sent when a download has been resumed. If a download failed with an
 * error, the -userInfo dictionary of the error will contain an
 * NSURLSessionDownloadTaskResumeData key, whose value is the resume
 * data.
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
    NSLog(@"%s-----fileOffset:%lld,---expectedTotalBytes:%lld",__func__,fileOffset,expectedTotalBytes);
}

see also

联系作者: iOS逆向(公号:iosrev)


作者简介:CSDNios15.4正式版 博客专家认证丨全站 Top 50、华为云云享专家认证、iOS逆向公号号主


简历模板、技术互http 302助。关注我,都给你。

发表评论

提供最优质的资源集合

立即查看 了解详情