iOS开发-NSOperationQueue实现上传图片队列
在开发中,遇到发帖需要上传图片,需要上传队列,这时候用到了NSOperationQueue
NSOperation为控制任务状态、优先级、依赖关系以及任务管理提供了一种线程安全的结构。可以通过调用start方法来手动启动一个任务,或者把它加入到NSOperationQueue中,当它到达队列头部时自动启动。
NSOperation的三个执行状态
这三个是NSOperation生命周期中的三个互斥的状态。
NSOperationQueue,
self.operationQueue = [[NSOperationQueue alloc] init];
[self.operationQueue setMaxConcurrentOperationCount:1];
在NSOperationQueue添加一系列的NSOperation,addDependency会使当前的NSOperation依赖上一个NSOperation完成后执行相应的block操作。
NSOperation *operation = [[NSOperation alloc] init];
[self.operationQueue addOperation:operation];
for (NSInteger index = 0; index < self.files.count; index++) {
NSBlockOperation *theOperation = [NSBlockOperation blockOperationWithBlock:^{
[self uploadFileOperation:index];
}];
[theOperation addDependency:operation];
[self.operationQueue addOperation:theOperation];
operation = theOperation;
}
针对每一个上传的文件定义对应的文件model
SDUpFileModel.h
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, SDUpFileType) {
SDUpFileTypeImage = 0, //上传的图片类型
SDUpFileTypeVoice, //上传的音频类型
SDUpFileTypeVideo //上传的视频类型
};
/**
上传的资源类型model,image,voice,video
*/
@interface SDUpFileModel : NSObject
/**
上传的文件类型
*/
@property (nonatomic, assign) SDUpFileType fileType;
/**
上传到七牛的key
*/
@property (nonatomic, strong) NSString *key;
/**
上传所需要的token,七牛的token
*/
@property (strong, nonatomic) NSString *token;
/**
上传的filePath,文件本地路径
*/
@property (nonatomic, strong) NSString *filePath;
/**
上传所需要的参数,视频参数,图片参数,语音参数等,根据fileType,参数不一样
*/
@property (nonatomic, strong) NSDictionary *params;
@end
SDUpFileModel.m
#import "SDUpFileModel.h"
@implementation SDUpFileModel
@end
上传的功能使用AFNetworking上传功能
如
NSURL *url = [NSURL URLWithString:kQiniuUpHost];
AFHTTPSessionManager *operationManager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithObjectsAndKeys:theFile.token,@"token",
kQiniuUserAgent,@"User-Agent",
nil];
if (theFile.key){
[parameters setObject:theFile.key forKey:@"key"];
[parameters setObject:url forKey:theFile.key];
}
if (theFile.params){
for (NSString *key in theFile.params){
[parameters setObject:[theFile.params objectForKey:key] forKey:key];
}
}
NSMutableURLRequest *request = [operationManager.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:kQiniuUpHost parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
if (theFile.fileType == SDUpFileTypeImage) {
//上传图片文件
[formData appendPartWithFileURL:[NSURL fileURLWithPath:theFile.filePath] name:@"file" fileName:@"file" mimeType:@"image/jpeg" error:nil];
} else if (theFile.fileType == SDUpFileTypeVoice) {
//语音文件
[formData appendPartWithFileURL:[NSURL fileURLWithPath:theFile.filePath] name:@"file" fileName:@"file" mimeType:@"audio/amr" error:nil];
} else if (theFile.fileType == SDUpFileTypeVideo) {
//mp4视频文件
[formData appendPartWithFileURL:[NSURL fileURLWithPath:theFile.filePath] name:@"file" fileName:@"file" mimeType:@"video/mpeg4" error:nil];
}
} error:nil];
__block NSURLSessionUploadTask *task = nil;
task = [operationManager uploadTaskWithStreamedRequest:request progress:^(NSProgress * _Nonnull uploadProgress) {
float percent = uploadProgress.completedUnitCount * 1.0 / uploadProgress.totalUnitCount;
if (self.upProgressHandler) {
self.upProgressHandler(theFile, percent);
}
} completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
NSHTTPURLResponse *taskresponse = (NSHTTPURLResponse *)task.response;
if(error == nil) {
NSInteger statusCode = taskresponse.statusCode;
if (statusCode == 200){
if (index == self.files.count -1) {
if (self.upAllComplectionHandler) {
self.upAllComplectionHandler(self);
}
}
} else{
if (self.upFilesFailedHandler) {
self.upFilesFailedHandler(self);
}
[self cancelAllUploadTask];
}
}else {
NSString *errorStr = [responseObject objectForKey:@"error"];
if ([errorStr isEqualToString:@"expired token"]) {
if (self.upFilesTokenErrorHandler) {
self.upFilesTokenErrorHandler(self);
}
} else {
if (self.upFilesFailedHandler) {
self.upFilesFailedHandler(self);
}
}
[self cancelAllUploadTask];
}
}];
[task resume];
上传工具的完整代码如下
SDUploaderTool.h
#import <Foundation/Foundation.h>
#import "SDUpFileModel.h"
@class SDUploaderTool;
/**
上传单个文件进度的block
*/
typedef void (^UploadProgressHandler)(SDUpFileModel *fileModel, float percent);
/**
上传所有文件成功的block
*/
typedef void (^UploadAllCompletionHandler)(SDUploaderTool *uploader);
/**
上传文件失败的block,上传文件的token异常,重新上传,重试3次
*/
typedef void (^UploadFilesTokenErrorHandler)(SDUploaderTool *uploader);
/**
上传文件失败的block
*/
typedef void (^UploadFilesFailedHandler)(SDUploaderTool *uploader);
@interface SDUploaderTool : NSObject
/**
上传单个文件进度的block
*/
@property (nonatomic, copy) UploadProgressHandler upProgressHandler;
/**
上传所有文件成功的block
*/
@property (nonatomic, copy) UploadAllCompletionHandler upAllComplectionHandler;
/**
上传文件失败的block,上传文件的token异常,重新上传,重试3次
*/
@property (nonatomic, copy) UploadFilesTokenErrorHandler upFilesTokenErrorHandler;
/**
上传文件失败的block
*/
@property (nonatomic, copy) UploadFilesFailedHandler upFilesFailedHandler;
/**
上传的资源列表,其中存储的是SDUpFileModel的对象
*/
@property (nonatomic, strong) NSMutableArray *files;
/**
添加上传文件
@param file SDUpFileModel
*/
- (void)addFile:(SDUpFileModel *)file;
/**
添加多个上传文件
@param theFiles 文件列表
*/
- (void)addFiles:(NSArray *)theFiles;
/**
开始上传,启动上传
*/
- (void)startUpload;
/**
取消所有的上传请求
*/
- (void)cancelAllUploadTask;
@end
SDUploaderTool.m
#import "SDUploaderTool.h"
#import <AFNetworking.h>
#define kQiniuUpHost @"http://up.qiniu.com"
#define kQiniuUndefinedKey @"?"
#define kQiniuUserAgent @"qiniu-ios-sdk"
@interface SDUploaderTool ()
@property (nonatomic, strong) NSOperationQueue *operationQueue;
@end
@implementation SDUploaderTool
- (instancetype)init
{
self = [super init];
if (self) {
self.files = [[NSMutableArray alloc] init];
self.operationQueue = [[NSOperationQueue alloc] init];
[self.operationQueue setMaxConcurrentOperationCount:1];
}
return self;
}
/**
添加上传文件
@param file SDUpFileModel
*/
- (void)addFile:(SDUpFileModel *)file {
[self.files addObject:file];
}
/**
添加多个上传文件
@param theFiles 文件列表
*/
- (void)addFiles:(NSArray *)theFiles {
[self.files addObjectsFromArray:theFiles];
}
/**
开始上传,启动上传
*/
- (void)startUpload {
if (!(self.files && self.files.count > 0)) {
return;
}
NSOperation *operation = [[NSOperation alloc] init];
[self.operationQueue addOperation:operation];
for (NSInteger index = 0; index < self.files.count; index++) {
NSBlockOperation *theOperation = [NSBlockOperation blockOperationWithBlock:^{
[self uploadFileOperation:index];
}];
[theOperation addDependency:operation];
[self.operationQueue addOperation:theOperation];
operation = theOperation;
}
}
/**
取消所有的上传请求
*/
- (void)cancelAllUploadTask {
[self.operationQueue cancelAllOperations];
}
/**
执行上传,使用AFNetWorking
@param index index
*/
- (void)uploadFileOperation:(NSInteger)index {
SDUpFileModel *theFile = self.files[index];
NSURL *url = [NSURL URLWithString:kQiniuUpHost];
AFHTTPSessionManager *operationManager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithObjectsAndKeys:theFile.token,@"token",
kQiniuUserAgent,@"User-Agent",
nil];
if (theFile.key){
[parameters setObject:theFile.key forKey:@"key"];
[parameters setObject:url forKey:theFile.key];
}
if (theFile.params){
for (NSString *key in theFile.params){
[parameters setObject:[theFile.params objectForKey:key] forKey:key];
}
}
NSMutableURLRequest *request = [operationManager.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:kQiniuUpHost parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
if (theFile.fileType == SDUpFileTypeImage) {
//上传图片文件
[formData appendPartWithFileURL:[NSURL fileURLWithPath:theFile.filePath] name:@"file" fileName:@"file" mimeType:@"image/jpeg" error:nil];
} else if (theFile.fileType == SDUpFileTypeVoice) {
//语音文件
[formData appendPartWithFileURL:[NSURL fileURLWithPath:theFile.filePath] name:@"file" fileName:@"file" mimeType:@"audio/amr" error:nil];
} else if (theFile.fileType == SDUpFileTypeVideo) {
//mp4视频文件
[formData appendPartWithFileURL:[NSURL fileURLWithPath:theFile.filePath] name:@"file" fileName:@"file" mimeType:@"video/mpeg4" error:nil];
}
} error:nil];
__block NSURLSessionUploadTask *task = nil;
task = [operationManager uploadTaskWithStreamedRequest:request progress:^(NSProgress * _Nonnull uploadProgress) {
float percent = uploadProgress.completedUnitCount * 1.0 / uploadProgress.totalUnitCount;
if (self.upProgressHandler) {
self.upProgressHandler(theFile, percent);
}
} completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
NSHTTPURLResponse *taskresponse = (NSHTTPURLResponse *)task.response;
if(error == nil) {
NSInteger statusCode = taskresponse.statusCode;
if (statusCode == 200){
if (index == self.files.count -1) {
if (self.upAllComplectionHandler) {
self.upAllComplectionHandler(self);
}
}
} else{
if (self.upFilesFailedHandler) {
self.upFilesFailedHandler(self);
}
[self cancelAllUploadTask];
}
}else {
NSString *errorStr = [responseObject objectForKey:@"error"];
if ([errorStr isEqualToString:@"expired token"]) {
if (self.upFilesTokenErrorHandler) {
self.upFilesTokenErrorHandler(self);
}
} else {
if (self.upFilesFailedHandler) {
self.upFilesFailedHandler(self);
}
}
[self cancelAllUploadTask];
}
}];
[task resume];
}
/**
释放
*/
- (void)dealloc {
self.files = nil;
[self.operationQueue cancelAllOperations];
self.operationQueue = nil;
}
@end
iOS开发-NSOperationQueue实现上传图片队列。使用NSOperationQueue,NSOperation,通过AFnetworking上传功能实现。
学习记录,每天不停进步。