美文网首页iOS程序员的业余沙龙iosiOS Review
iOS-AFNetworking3.0上传大量(1000张)图片

iOS-AFNetworking3.0上传大量(1000张)图片

作者: charlotte2018 | 来源:发表于2017-03-26 17:42 被阅读1518次

    背景:

    最近项目要做上传图片功能,图片必须是高清的,所以不让压缩,上传图片是大量的,比如几百张,这个如果是用afn,将图片直接for循环加入到formData里会出现一个问题,临时变量太多,导致内存紧张,最后程序奔溃。之前写过用自动释放池解决它,但是还是效果不大。如果上传的多的话,内存还是受不了。

    解决办法一适用于图片少量的如40张图片

    我之前写的在这,可以看看自动释放池的方法,如果你上传图片的数量不多的话,可以用这种方法。也很简单的。链接在这里http://www.jianshu.com/p/9e84fe63d5c0

    解决办法二适用于图片大量的如1000张图片

    思考,为甚内存会占用那么多呢?就是因为图片一口气读到内存中了。如果咱们上传三五张图片,肯定不会出问题。如何把1000张图片分开传呢?所以必须要用到多线程的知识。创建个队列。然后挨个传。注意不要把文件存到队列里,只要先存一个文件名,执行的时候再去读取文件的内容。如果要是将image传给队列,内存还是会爆的。所以存个图片名字。一个字符串肯定没有image占用的内存大吧。上代码吧。我的图片来源于相册,所以我用的图片id。

    /**
     创建队列然后开始上传图片
    
     @param LocalIdArray 获取相册的图片id数组,如果你是本地的就传递图片名字数组,或者是沙盒的文件名字数组
     */
    - (void)uploadOperation:(NSArray *)LocalIdArray
    {
        
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        self.queue = queue;
        //这个就是控制同时上传几张图片的,如果是1的话就是串行队列了。我是4,是并行队列。
        queue.maxConcurrentOperationCount = 4;
        
        
        for (int i = 0; i<LocalIdArray.count; i++)
        {
            //加上自动释放池,及时的释放临时变量,防止内存奔溃
            @autoreleasepool {
                
                NSString *imageName = [NSString stringWithFormat:@"up_%d.jpg",i];
                
                WS(weakSelf)
                //创建一个任务
                NSBlockOperation *uploadOperation = [NSBlockOperation blockOperationWithBlock:^{
                    
                    [weakSelf uploadTaskWithLocalId:LocalIdArray[i] imageount:LocalIdArray.count imageName:imageName];
                }];
                //将任务加入到队列中
                [queue addOperation:uploadOperation];
            }
            
        }
        
        
    }
    
    
    /**
     开始上传单张图片
    
     @param LocalId 图片的id
     @param count 一共上传多少张图片
     @param imageName 图片的名称
     */
    - (void)uploadTaskWithLocalId:(NSString *)LocalId imageount:(NSInteger)count imageName:(NSString *)imageName
    {
        
        //通过图片的id转化为image,如果是图片名字或者是沙盒图片文件名字那更简单了。
        CustomAlbumTool *customAlbumTool = [CustomAlbumTool sharedCustomAlbumTool];
        PHFetchResult<PHAsset *> *upAssetArr = [PHAsset fetchAssetsWithLocalIdentifiers:@[LocalId] options:nil];
        PHAsset *asset = [upAssetArr firstObject];
        UIImage *image = [customAlbumTool getImageWithAsset:asset targetSize:PHImageManagerMaximumSize];
        
        //afn上传的参数
        NSMutableDictionary *dic = [NSMutableDictionary dictionary];
        dic[@"xxx"] = [UserDataCenter xxx];
        dic[@"xxx"] = self.xxx;
        
        //因为afn上传是异步执行的所以创建一个信号量。就是为了让一个任务完全的执行完毕后才执行下一个任务。加信号量就是为了把afn异步转化为同步。如果不转化的话。queue.maxConcurrentOperationCount = 1,也没办法做到队列内同步。
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        
        
        WS(weakSelf)
        [SWAYNetWorking uploadWithUrl:uploadModelUrl parameters:dic constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
            
            NSData *data = UIImageJPEGRepresentation(image, 1.0);
            [formData appendPartWithFileData:data name:@"file" fileName:imageName mimeType:@"image/jpeg"];
            
        } withProgress:^(NSProgress *uploadProgress) {
            
        } success:^(id responseObject) {
            
            //图片成功了让信号量加1
            dispatch_semaphore_signal(semaphore);
            
        } failure:^(NSError *error) {
            
            //图片传失败了让信号量加1
            dispatch_semaphore_signal(semaphore);
            
        }];
        
        //信号量等待。DISPATCH_TIME_FOREVER 永远等到吧。
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        
    }
    
    

    监听全部成功刷新ui,可以定义个int型变量,上传成功一张图片加1。如果等于总的图片数量就相当于上传完成了,那么就刷新UI吧。也可以创建个上传成功刷新UI的任务。添加依赖。在任务里执行刷新UI。

    /**
     创建队列然后开始上传图片
    
     @param LocalIdArray 获取相册的图片id数组,如果你是本地的就传递图片名字数组,或者是沙盒的文件名字数组
     */
    - (void)uploadOperation:(NSArray *)LocalIdArray
    {
    
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        self.queue = queue;
        //这个就是控制同时上传几张图片的,如果是1的话就是串行队列了。我是4,是并行队列。
        queue.maxConcurrentOperationCount = 4;
    
        NSBlockOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // 回到主线程执行,方便更新 UI 等
              
            }];
        }];
    
        for (int i = 0; i<LocalIdArray.count; i++)
        {
            //加上自动释放池,及时的释放临时变量,防止内存奔溃
            @autoreleasepool {
    
                NSString *imageName = [NSString stringWithFormat:@"up_%d.jpg",i];
    
                WS(weakSelf)
                //创建一个任务
                NSBlockOperation *uploadOperation = [NSBlockOperation blockOperationWithBlock:^{
    
                    [weakSelf uploadTaskWithLocalId:LocalIdArray[i] imageount:LocalIdArray.count imageName:imageName];
                }];
               //添加依赖。
                [completionOperation addDependency:uploadOperation];
                //将任务加入到队列中
                [queue addOperation:uploadOperation];
            }
    
        }
         //将刷新UI的任务加入队列,当所有的上传任务结束才会调用completionOperation。
         [queue addOperation:completionOperation];
    
    
    }
    
    

    如果要是你的业务是不能让一张图片传递失败,那么当有一张图没有传成功的话就直接取消所有任务就行了。

    [weakSelf.queue cancelAllOperations];
    

    相关文章

      网友评论

      • lsif的简书:请问楼主, cancelAllOperations这个方法不能终止传输可能因为什么?
      • smkoc:这样处理好吗 ? 我感觉不好
      • 44bb394883a7:CustomAlbumTool这个你中间是怎么处理的,能不能提供个demo?万分感谢!
      • b944fafb361d:如果上传成功后 在UIcollectionView展示 呢
        b944fafb361d:@王银博 我想请问下 如何从相册选一百张照片而不崩溃呢?然后按照您说的办法上传,完毕之后 在在UI collectionview展示刚刚上传的照片
        b944fafb361d:@王银博 展示刚刚上传的照片
        charlotte2018:@HE铭 展示什么?
      • 720841baa847:如果有一张失败了呢?
        charlotte2018:@马旭_愿你成为自己的太阳 没,这就是项目的一小部分
        charlotte2018:@ping222s 看自己业务,如果一张都不能失败,那有一张失败,直接取消任务好了。

      本文标题:iOS-AFNetworking3.0上传大量(1000张)图片

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