1. 程式人生 > >iOS-AFNetworking3.0上傳大量(1000張)圖片到服務器

iOS-AFNetworking3.0上傳大量(1000張)圖片到服務器

gre top defined 內存 agen con 完全 任務 自動

背景:

最近項目要做上傳圖片功能,圖片必須是高清的,所以不讓壓縮,上傳圖片是大量的,比如幾百張,這個如果是用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];


作者:王銀博
鏈接:http://www.jianshu.com/p/5162df747879
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。

iOS-AFNetworking3.0上傳大量(1000張)圖片到服務器