美文网首页
iOS阿里云多图片上传

iOS阿里云多图片上传

作者: HCL黄 | 来源:发表于2019-10-21 18:20 被阅读0次

    前提,阿里云文件上传文档已经了解过

    我们在上传多图片的时候,通常会遇到如下问题:

    • 1、图片如何有序的上传?
    • 2、如何保证所有图片全部上传完毕,这样才可以继续其他操作?
    很多人可能会说开启同步任务,这样就可以保证有序并且也能保证所有图片都上传完毕了,但是同步任务会阻塞当前线程,这样可能会诱发不确定因素。
    所以我接下去要讲的是利用异步任务来进行多图片上传

    第一步,先初始化阿里云OSS

    #import <UIKit/UIKit.h>
    #import "OSSService.h"
    @interface HttpTool : NSObject
    // 单例
    + (HttpTool *)sharedInstance;
    // 初始化OSS
    + (void)initOSSClient:(NSString *)tokenUrl;
    
    @property (nonatomic, strong) OSSClient *client;
    @end
    
    #import "HttpTool.h"
    @implementation HttpTool
    + (HttpTool *)sharedInstance{
        
        // 设nil
        static HttpTool *_sharedInstance = nil;
        // 只执行一次
        static dispatch_once_t oncePredicate;
        // 初始化
        dispatch_once(&oncePredicate, ^{
            _sharedInstance = [[HttpTool alloc] init];
        });
        return _sharedInstance;
    }
    #pragma mark - 初始化oss
    + (void)initOSSClient:(NSString *)tokenUrl {
        
        //    [OSSLog enableLog];
        id<OSSCredentialProvider> credential = [[OSSFederationCredentialProvider alloc] initWithFederationTokenGetter:^OSSFederationToken * {
            NSURL *url = [NSURL URLWithString:tokenUrl];
            NSURLRequest *request = [NSURLRequest requestWithURL:url];
            OSSTaskCompletionSource * tcs = [OSSTaskCompletionSource taskCompletionSource];
            NSURLSession *session = [NSURLSession sharedSession];
            NSURLSessionTask * sessionTask = [session dataTaskWithRequest:request
                                                        completionHandler:^(NSData *data,
                                                                            NSURLResponse *response,
                                                                            NSError *error) {
                                                            if (error) {
                                                                [tcs setError:error];
                                                                return;
                                                            }
                                                            [tcs setResult:data];
                                                        }];
            [sessionTask resume];
            [tcs.task waitUntilFinished];
            if (tcs.task.error) {
                
                NSLog(@"get token error: %@", tcs.task.error);
                return nil;
            } else {
                
                NSDictionary *object = [NSJSONSerialization JSONObjectWithData:tcs.task.result
                                                                       options:kNilOptions
                                                                         error:nil];
                OSSFederationToken *token = [OSSFederationToken new];
                token.tAccessKey = [object objectForKey:@"AccessKeyId"];
                token.tSecretKey = [object objectForKey:@"AccessKeySecret"];
                token.tToken = [object objectForKey:@"SecurityToken"];
                token.expirationTimeInGMTFormat = [object objectForKey:@"Expiration"];
                NSLog(@"get token: %@", token);
                return token;
            }
        }];
        
        OSSClientConfiguration *conf = [OSSClientConfiguration new];
        conf.maxRetryCount = 3; // 网络请求遇到异常失败后的重试次数
        conf.timeoutIntervalForRequest = 20; // 网络请求的超时时间
        conf.timeoutIntervalForResource = 24*60*60; // 允许资源传输的最长时间
        conf.maxConcurrentRequestCount = 30;
        
        [HttpTool sharedInstance].client = [[OSSClient alloc] initWithEndpoint:OSS_endPoint credentialProvider:credential clientConfiguration:conf];
    }
    
    @end
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 初始化oss
        [HttpTool initOSSClient:OSS_FederationToken_video];
    }
    

    第二步,我们来弄个任务类,专门处理阿里云多图片上传

    #import <UIKit/UIKit.h>
    #import "OSSService.h"
    
    typedef void (^OSSCallBackBlock)(OSSTask * _Nullable task);
    NS_ASSUME_NONNULL_BEGIN
    
    @interface LAHttpTask : NSObject
    /** 从内存中的NSData上传请求 */
    @property (nonatomic, strong) OSSPutObjectRequest *dataPut;
    /** 从文件上传时请求 */
    @property (nonatomic, strong) OSSPutObjectRequest *filePut;
    /**
     bucketName: 存储空间的名字
     objectKey: 文件夹路径
     */
    - (void)startUploadImageData:(NSData *)imageData imaegW:(CGFloat)imageW imageH:(CGFloat)imageH bucketName:(NSString *)bucketName objectKey:(NSString *)objectKey callBack:(OSSCallBackBlock)callBack;
    /**
     fileURL: 文件路径url
     */
    - (void)startUploadFileURL:(NSURL *)fileURL bucketName:(NSString *)bucketName objectKey:(NSString *)objectKey callBack:(OSSCallBackBlock)callBack;
    @end
    
    NS_ASSUME_NONNULL_END
    
    #import "LAHttpTask.h"
    
    @interface LAHttpTask ()
    
    @end
    
    @implementation LAHttpTask
    /**
     bucketName: 存储空间的名字
     objectKey: 文件夹路径
     */
    - (void)startUploadImageData:(NSData *)imageData imaegW:(CGFloat)imageW imageH:(CGFloat)imageH bucketName:(NSString *)bucketName objectKey:(NSString *)objectKey callBack:(OSSCallBackBlock)callBack {
        self.dataPut = [OSSPutObjectRequest new];
        self.dataPut.bucketName = bucketName;
        self.dataPut.objectKey = objectKey;
        self.dataPut.uploadingData = imageData;
        self.dataPut.contentType = @"";
        self.dataPut.contentMd5 = @"";
        self.dataPut.contentEncoding = OSS_endPoint;
        self.dataPut.contentDisposition = @"";
        self.dataPut.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
            //        NSLog(@"%lld, %lld, %lld", bytesSent, totalByteSent, totalBytesExpectedToSend);
        };
        
        OSSTask * putTask = [[HttpTool sharedInstance].client putObject:self.dataPut];
        [putTask continueWithBlock:^id(OSSTask *task) {
            if (callBack) {
                callBack(task);
            }
            return nil;
        }];
    }
    
    - (void)startUploadFileURL:(NSURL *)fileURL bucketName:(NSString *)bucketName objectKey:(NSString *)objectKey callBack:(OSSCallBackBlock)callBack {
        
        self.filePut = [OSSPutObjectRequest new];
        // required fields
        self.filePut.bucketName = bucketName;
        self.filePut.objectKey = objectKey;
        self.filePut.uploadingFileURL = fileURL;
        // optional fields
        self.filePut.contentType = @"";
        self.filePut.contentMd5 = @"";
        self.filePut.contentEncoding = OSS_endPoint;
        self.filePut.contentDisposition = @"";
        OSSTask * putTask = [[HttpTool sharedInstance].client putObject:self.filePut];
        [putTask continueWithBlock:^id(OSSTask *task) {
            if (callBack) {
                callBack(task);
            }
            return nil;
        }];
    }
    
    @end
    

    第三步,我们来弄个任务管理器,专门处理每个图片对应每个LAHttpTask

    @interface LAMissionManager : NSObject
    
    /**
     images: 图片数组
     folderPath: 文件路径,如:video/subVideo/
     urlPre: url前缀,如:OSS_VideoPre
     bucketName: 磁盘名称,如:OSS_BucketName_video
     */
    - (void)startMissionWithImages:(NSArray<UIImage *> *)images folderPath:(NSString *)folderPath urlPre:(NSString *)urlPre bucketName:(NSString *)bucketName callback:(void(^)(BOOL success,NSArray *imageUrls))callback;
    @end
    
    typedef void(^Callback)(BOOL success,NSArray *imageUrls);
    
    @interface LAMissionManager ()
    
    /**超时定时器*/
    @property (nonatomic,strong) NSTimer *timer;
    /** 保存传过来的图片数组 */
    @property (nonatomic,strong) NSArray *images;
    /** 完成回调 */
    @property (nonatomic,strong) Callback callback;
    /** 任务数组 */
    @property (nonatomic,strong) NSMutableArray *imageTaskM;
    /** 上传成功之后的图片链接数组 */
    @property (nonatomic,strong) NSMutableArray *uploadSucceedImageM;
    
    @end
    
    @implementation LAMissionManager
    /**
     images: 图片数组
     folderPath: 文件路径,如:video/subVideo/
     urlPre: url前缀,如:OSS_VideoPre
     bucketName: 磁盘名称,如:OSS_BucketName_video
     */
    - (void)startMissionWithImages:(NSArray<UIImage *> *)images folderPath:(NSString *)folderPath urlPre:(NSString *)urlPre bucketName:(NSString *)bucketName callback:(void(^)(BOOL success,NSArray *imageUrls))callback {
        self.images         = images;
        self.callback       = callback;
        //开启超时定时器
        self.timer = [NSTimer scheduledTimerWithTimeInterval:100 target:self selector:@selector(timeoutAction:) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
         
        // 清空数据
        [self.uploadSucceedImageM removeAllObjects];
        // 遍历图片数组
        for (NSInteger i = 0; i < self.images.count; i++) {
            UIImage *image = self.images[i];
            // 创建阿里云上传任务,一个图片对应一个任务
            LAHttpTask *task = [[LAHttpTask alloc] init];
            // 添加到图片任务数组
            [self.imageTaskM addObject:task];
            CGFloat imaegW = image.size.width;
            CGFloat imaegH = image.size.height;
            // 图片命名
            NSString *fileName = [NSString stringWithFormat:@"%@.png", [Tools_F deviceUUID]];
            // 压缩图片
            image = [GGUtil compressImage:image];
            NSData *imageData = UIImagePNGRepresentation(image);
            // 上传文件时,如果把 ObjectKey 写为"folder/subfolder/file",即是模拟了把文件上传到folder/subfolder/下的file文件。注意,路径默认是根目录,不需要以正斜线 / 开头。
            NSString *objectKey = [NSString stringWithFormat:@"%@%@%@",folderPath,[Tools_F getCurrentUploadYMDHMS],fileName];
            // OSS任务回调block
            OSSCallBackBlock takCallBack = ^(OSSTask *task) {
                if (!task.error) {
                    NSLog(@"upload success! image");
                    // 拼接完整图片链接
                    NSString *urlStr = [NSString stringWithFormat:@"%@%@",urlPre,objectKey];
                    // 拼接图片宽高及排序参数,宽高参数是为了后续后台返回图片url的时候我们可以直接拿到对应的宽高,sort参数是为了有序的上传给我们自己的后台
                    urlStr = [NSString stringWithFormat:@"%@?iW=%.f&iH=%.f?sort=%ld",urlStr,imaegW,imaegH,(long)i];
                    // 添加到上传成功的数组
                    [self.uploadSucceedImageM addObject:urlStr];   
                    // 当uploadSucceedImageM与images相等时,说明所有图片上传完毕
                    if (self.uploadSucceedImageM.count == self.images.count) {
                        //停止定时器
                        [self.timer invalidate];
                        // 标志上传完毕,并且对已上传的图片链接进行排序处理
                        [self complete:YES imgUrls:self.uploadSucceedImageM];
                    }
                }
                else {
                    NSLog(@"upload failed image, error: %@" , task.error);
                }
            };
            // 开启上传图片任务
            [task startUploadImageData:imageData imaegW:imaegW imageH:imaegH bucketName:bucketName objectKey:objectKey callBack:takCallBack];
        }
    }
    
    - (void)complete:(BOOL)isSuccess imgUrls:(NSMutableArray *)imgUrls {
        if (self.callback) {
            if (imgUrls.count == 0) {
                // 没有图片链接,直接标志失败,返回
                self.callback(NO, imgUrls);
                return;
            }
            NSLog(@"complete imgUrls = %@",imgUrls);
            NSMutableArray *imgIndexM = [NSMutableArray array];
            NSMutableDictionary *imgDicM = [NSMutableDictionary dictionary];
            // 遍历图片链接数组,这里是无序,因为上面我们用的是异步上传
            for (NSInteger i = 0; i < imgUrls.count; i++) {
                NSString *urlString = imgUrls[i];
                // 取出的图片链接是否包含  ?sort=
                if ([urlString containsString:@"?sort="]) {
                    // firstObjc = @“https://123.png”;
                    NSString *firstObjc = [[urlString componentsSeparatedByString:@"?sort="] firstObject];
                    // lastObjc = @“0”;
                    NSString *lastObjc = [[urlString componentsSeparatedByString:@"?sort="] lastObject];
                    [imgIndexM addObject:@(lastObjc.integerValue)]; // [2,5,1,3,4]
                    [imgDicM setObject:firstObjc forKey:lastObjc]; // {@"2": @"https://123.png"}
                }else{  // 没有包含就直接随便加个值,注意不能在imgUrls范围内取值
                    [imgIndexM addObject:@(1000)];
                    [imgDicM setObject:urlString forKey:@(1000).stringValue];  // {@"1000": @"https://123.png"}
                }
            }
            // 对存储sort值的imgIndexM进行排序
            NSArray *imgResult = [imgIndexM sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
                return [obj1 compare:obj2];
            }];
            NSMutableArray *imgResultUrlM = [NSMutableArray array];
            /**
            imgDicM:[
                              {@"3":@"https://3.png"},
                              {@"4":@"https://4.png"},
                              {@"2":@"https://2.png"},
                              ]
            imgResult: [@"2",@"3",@"4"]
            通过正确的排序,去取imgDicM对应key的值,然后存储
            */
            for (NSNumber *resultIndex in imgResult) {
                if (resultIndex.integerValue == 1000) {
                    [imgResultUrlM addObject:[imgDicM valueForKey:@(1000).stringValue]];
                }else{
                    [imgResultUrlM addObject:[imgDicM valueForKey:[resultIndex stringValue]]];
                }
            }
            self.callback(YES, imgResultUrlM);
        }
    }
    

    第四步,使用TZImagePickerController进行本地图片选择,具体使用大家可以自己去了解https://github.com/banchichen/TZImagePickerController

    /** 多选视频或图片完成时调用 */
    - (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto {
        NSLog(@"***************多选视频或图片完成时调用*****************");
        // 获取assets原图
        [self getOriginalPhotoWithScript:assets];
    }
    
    - (void)getOriginalPhotoWithScript:(NSArray *)assets {
        /**
          以下代码是项目需求,排序传值到H5
          当然也可以不用这么麻烦,直接遍历assets,拿到原图,存到数组就可以了,
        */
        NSMutableDictionary *imgDicM = [NSMutableDictionary dictionary];
        // 创建一个线程组
        dispatch_group_t group = dispatch_group_create();
        // 获取全局队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        // 这是一个HUD
        [MBProgressHUD showMessage:@"导入图片中..." toView:kWindow];
        // 遍历所有assets
        for (NSInteger i = 0; i < assets.count; i++) {
            PHAsset *asset = assets[i];
            // 任务开始加入group
            dispatch_group_enter(group);
            // 开启异步任务
            dispatch_async(queue, ^{
                // 获取原图
                [[TZImageManager manager] getOriginalPhotoWithAsset:asset completion:^(UIImage *photo, NSDictionary *info) {
                    if (!isDegraded) { // 原图
                        // 任务完成,移除
                        dispatch_group_leave(group);
                        if (photo) {
                            // 这里我使用的是对象模型来处理image,当然你也可以直接使用image
                            LAImageModel *model = [[LAImageModel alloc] init];
                            model.image = photo;
                            // 将对象模型作为值,下标i字符串作为key,存储到imgDicM
                            // 至于为什么要这么存储,当然是下面会用到啦
                            [imgDicM setObject:model forKey:@(i).stringValue];
                        }
                    }
                }];
            });
        }
        // 当全部任务完成之后,就会调用下面的方法
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            // 等前面的异步操作都执行完毕后,回到主线程.
            NSLog(@"imgDicM = %@",imgDicM);
            // 隐藏HUD
            [MBProgressHUD hideHUD];
            // 初始化图片下标的数组
            NSMutableArray *imgIndexM = [NSMutableArray array];
            // 遍历imgDicM
            [imgDicM enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL * _Nonnull stop) {
                // 将imgDicM的key取出来,注意这里key是无序的
                [imgIndexM addObject:@(key.integerValue)];
            }];
            // 接下来我们先对图片下标进行排序
            NSArray *imgSortResults = [imgIndexM sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
                return [obj1 compare:obj2];
            }];
            // 重新整合有序的数据
            for (NSNumber *resultIndex in imgSortResults) {
                LAImageModel *model = [imgDicM valueForKey:[resultIndex stringValue]];
                // 将来要提交给后台的
                [self.exportScriptImgeM addObject:model.image];
            }
            NSLog(@"exportScriptImgeM = %@",self.exportScriptImgeM);
        });
    }
    

    第五步,点击提交上传

    - (void)scriptSubmitAction {
        if (self.exportScriptImgeM.count == 0) {
            [self showText:@"您还没有选择图片"];
            return;
        }
        // 处理日期
        NSArray *dates = [self dealWithDate];
        [MBProgressHUD showMessage:@"提交中..."];
        @weakify(self);
        [self.manager startMissionWithImages:self.exportScriptImgeM callback:^(BOOL success, NSArray * _Nonnull imageUrls) {
            @strongify(self);
            NSLog(@"[x] imageUrls = %@",imageUrls);
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                [MBProgressHUD hideHUD];
            }];
            if (success) {
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    [self showText:@"上传成功"];
                    // 接下来就可以发给自己的后台了
                    。。。
                }];
            } else {
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    [self showText:@"上传失败"];
                }];
            }
        }];
    }
    

    相关文章

      网友评论

          本文标题:iOS阿里云多图片上传

          本文链接:https://www.haomeiwen.com/subject/wobtvctx.html