iOS开发-NSOperationQueue实现上传图片队列

iOS开发-NSOperationQueue实现上传图片队列

在开发中,遇到发帖需要上传图片,需要上传队列,这时候用到了NSOperationQueue

一、NSOperation与NSOperationQueue

  • 什么NSOperation

NSOperation为控制任务状态、优先级、依赖关系以及任务管理提供了一种线程安全的结构。可以通过调用start方法来手动启动一个任务,或者把它加入到NSOperationQueue中,当它到达队列头部时自动启动。

  • NSOperation的三个执行状态

    • isReady、
    • isExecuting、
    • isFinished

这三个是NSOperation生命周期中的三个互斥的状态。

二、通过NSOperationQueue添加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上传功能实现。

学习记录,每天不停进步。

你可能感兴趣的:(移动开发,iphone开发,Objective-c,ios,上传队列,NSOperation,AFnetworking)