iOS开发基础42-网络编程之文件下载与处理
在iOS应用开发中,文件下载是一个常见的需求,可能涉及下载小图片、音频文件或者大型的视频、压缩包等。本文将详细介绍如何在iOS中进行文件下载,包括小文件和大文件的下载处理,文件的压缩/解压缩,获取文件MIMEType,以及文件上传和断点续传。
一、小文件下载
对于小文件的下载,可以直接使用NSURLConnection
的异步请求方法:
NSURL *url = [NSURL URLWithString:@"http://example.com/path/to/file.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (data) {
UIImage *image = [UIImage imageWithData:data];
// 将图片用于界面展示或保存等操作
} else {
// 错误处理
NSLog(@"Download failed with error: %@", connectionError);
}
}];
二、大文件下载
对于大文件的下载,直接使用内存处理会导致内存暴涨,因此需要更高效的方式来处理大文件的下载。下面介绍三种处理方式:
1. 使用 NSMutableData
这种方法简单但会导致内存暴涨,不推荐:
@property (nonatomic, strong) NSMutableData *fileData;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.fileData appendData:data];
}
2. 使用 NSFileHandle
NSFileHandle
是专门用于文件操作的类,通过创建文件句柄,将下载的数据写入文件,节省内存:
@interface ViewController ()<NSURLConnectionDataDelegate>
@property (nonatomic, strong) NSFileHandle *fileHandle;
@property (nonatomic, copy) NSString *filePath;
@property (nonatomic, assign) NSUInteger totalLength;
@property (nonatomic, assign) NSUInteger currentLength;
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:@"http://example.com/path/to/file.mp4"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSString *fileName = response.suggestedFilename;
self.filePath = [[self documentsDirectory] stringByAppendingPathComponent:fileName];
[[NSFileManager defaultManager] createFileAtPath:self.filePath contents:nil attributes:nil];
self.fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.filePath];
self.totalLength = response.expectedContentLength;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.fileHandle seekToEndOfFile];
[self.fileHandle writeData:data];
self.currentLength += data.length;
self.progressView.progress = (CGFloat)self.currentLength / self.totalLength;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.fileHandle closeFile];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"Download failed with error: %@", error);
}
- (NSString *)documentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
}
@end
3. 使用 NSOutputStream
NSOutputStream
是另一种处理大文件下载的方式,性能良好且内存占用少:
@property (nonatomic, strong) NSOutputStream *outputStream;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
self.filePath = [[self documentsDirectory] stringByAppendingPathComponent:response.suggestedFilename];
self.outputStream = [NSOutputStream outputStreamToFileAtPath:self.filePath append:YES];
[self.outputStream open];
self.totalLength = response.expectedContentLength;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.outputStream write:data.bytes maxLength:data.length];
self.currentLength += data.length;
self.progressView.progress = (CGFloat)self.currentLength / self.totalLength;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.outputStream close];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"Download failed with error: %@", error);
}
三、文件压缩/解压缩(使用第三方框架ZipArchive)
使用 ZipArchive
框架可以快速实现文件的压缩和解压缩:
压缩文件
NSArray *files = @[@"path/to/file1", @"path/to/file2"];
NSString *zipPath = @"path/to/archive.zip";
[SSZipArchive createZipFileAtPath:zipPath withFilesAtPaths:files];
解压缩文件
NSString *zipPath = @"path/to/archive.zip";
NSString *destinationPath = @"path/to/destination";
[SSZipArchive unzipFileAtPath:zipPath toDestination:destinationPath];
四、MIMEType(获取文件类型)
获取文件的MIMEType有两种方法:使用 NSURLConnection
和 C语言API。
使用 NSURLConnection
- (NSString *)MIMETypeForURL:(NSURL *)url {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
return response.MIMEType;
}
使用 C语言API
+ (NSString *)mimeTypeForFileAtPath:(NSString *)path {
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
return nil;
}
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)[path pathExtension], NULL);
CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType);
CFRelease(UTI);
if (!MIMEType) {
return @"application/octet-stream";
}
return (__bridge_transfer NSString *)MIMEType;
}
五、文件上传
文件上传需要构建特定的HTTP请求头和请求体。
设置请求头
[request setValue:@"multipart/form-data; boundary=Boundary" forHTTPHeaderField:@"Content-Type"];
构建请求体
NSMutableData *body = [NSMutableData data];
NSString *boundary = @"Boundary";
// 文件参数
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n", fileName] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimeType] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:fileData];
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
// 非文件参数
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Disposition: form-data; name=\"param\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"paramValue" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
// 结束标记
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
request.HTTPBody = body;
发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (data) {
// 上传成功处理
} else {
// 错误处理
NSLog(@"Upload failed with error: %@", connectionError);
}
}];
六、断点下载(续传)
断点下载的关键是通过自定义HTTP请求的头部的 Range
属性来实现:
NSURL *url = [NSURL URLWithString:@"http://example.com/path/to/file.mp4"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:@"bytes=20000-" forHTTPHeaderField:@"Range"];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
// 将数据写入文件
[self writeToFile:data filePath:@"path/to/local/file.mp4"];
写入数据到文件
- (void)writeToFile:(NSData *)data filePath:(NSString *)filePath {
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
[[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
}
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
[fileHandle seekToEndOfFile];
[fileHandle writeData:data];
[fileHandle closeFile];
}
将来的你会感谢今天如此努力的你!
版权声明:本文为博主原创文章,未经博主允许不得转载。