7

I have a task of uploading multiple images to the server one by one. So I am using the batch operation process for this. Every time I start the upload procedure, some operations specially the first one completes as soon as it starts and the image does not get uploaded, and then the batch upoad process continues fine with a rare glitch of missing the other images.

The code I am using is as follows:-

-(void)callWSToUploadRxs{


    NSLog(@"the total assets maintained are %lu", (unsigned long)_arr_assetsMaintained.count);

    NSMutableArray *mutableOperations = [NSMutableArray array];
    int imageUploadCount = (int)[self extractFullSizeImagesToUpload].count;
    // second for loop is to initialize the operations and then queue them.
    for (int i = 0; i<imageUploadCount; i++) {


        NSData *imageData = UIImageJPEGRepresentation([_arr_originalImagesToSend objectAtIndex:i],1.0);

        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
        [request setHTTPMethod:@"POST"];

        NSLog(@"the url constructed is %@", [NSString stringWithFormat:@"%@/%@/%@/%@",uploadRxUrl,@"4004DD85-1421-4992-A811-8E2F3B2E49F7",@"5293",[_arr_imageNames objectAtIndex:i]]);
        [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/%@/%@/%@.jpg",uploadRxUrl,@"4004DD85-1421-4992-A811-8E2F3B2E49F7",@"5293",[_arr_imageNames objectAtIndex:i]]]];
        [request setValue:@"binary/octet-stream" forHTTPHeaderField:@"Content-Type"];

        [request setHTTPBody:imageData];
        AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

        [mutableOperations addObject:operation];
    }

    currentUploadIndex++;
    NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:mutableOperations progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
        NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);

        NSIndexPath * indexOfImageTobeDeleted = [_selectedItemsIndexPaths objectAtIndex:0];//numberOfFinishedOperations-1
        [_arr_assetsMaintained removeObjectAtIndex:indexOfImageTobeDeleted.item];
        [_arr_images removeObjectAtIndex:indexOfImageTobeDeleted.item];
        [_arr_fullSizeImages removeObjectAtIndex:indexOfImageTobeDeleted.item];
        [_arr_imageNames removeObjectAtIndex:indexOfImageTobeDeleted.item];

        if ( [_arr_selectedCells containsObject:[NSString stringWithFormat:@"%ld",(long)indexOfImageTobeDeleted.item]]  )
        {
            [_arr_selectedCells removeObject:[NSString stringWithFormat:@"%ld",(long)indexOfImageTobeDeleted.item]];
            //[cell.img_selctedRxs setHidden:TRUE];


        }
        countBeforeClearingAssets = countBeforeClearingAssets - 1;
        //Reload the items of UICollectionView performBatchUpdates Block
        [_albumImagesCollection performBatchUpdates:^{
            [_albumImagesCollection deleteItemsAtIndexPaths:@[indexOfImageTobeDeleted]];
        } completion:nil];

        _selectedItemsIndexPaths = [_albumImagesCollection indexPathsForSelectedItems];
       // [_selectedItemsIndexPaths removeObjectAtIndex:0];
        NSLog(@"the count of selected items after updation is %lu", (unsigned long)_selectedItemsIndexPaths.count);


    } completionBlock:^(NSArray *operations) {
        NSLog(@"All operations in batch complete");
        [self callWSToAddNoteForRxs];
        [_arr_originalImagesToSend removeAllObjects];
        [_arr_selectedCells removeAllObjects];
        currentUploadIndex = 0;
        NSLog(@"the array of image names is %@",_arr_imageNames);
    }];

    [[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];

    // third is to maintain the progress block for each image to be uploaded one after the other.
    for (AFHTTPRequestOperation *operation in mutableOperations){

        [operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {

            [_progressOverLayView setAlpha:0.7f];
            [_progressView setHidden:FALSE];
            [_progressView setProgress: totalBytesWritten*1.0f / totalBytesExpectedToWrite animated: YES];
            [_lbl_progressUpdate setHidden:FALSE];
            _lbl_progressUpdate.text = [NSString stringWithFormat:@"Image %d of %lu uploading", currentUploadIndex, mutableOperations.count];
            NSLog(@"Sent %lld of %lld bytes and progress is %f", totalBytesWritten, totalBytesExpectedToWrite, totalBytesWritten*1.0f /  totalBytesExpectedToWrite);
            if(totalBytesWritten >= totalBytesExpectedToWrite)
            {
                //progressView.hidden = YES;
                [self setComplete];
            }
        }];
    }

}

In this code, the first operation is getting executed as soon I start uploading the images i.e only 3 out of 4 are getting uploaded. One Image is always left out. Also. if I do have only Image in the grid, that uploads successfully.

Thanks.

Shikhar varshney
  • 816
  • 1
  • 9
  • 23
  • Does anyone have a viable solution for this? – Shikhar varshney Dec 22 '15 at 13:21
  • Is the image that is being left out from the upload process, still deleted from albumImagesCollection? And also, it's the first image that is left out or the last one? – user2695712 Dec 30 '15 at 12:22
  • yes it is left out and the completion block for that image runs, which actually deletes it from collection view. It is mostly the first image of the two in 95% of the cases and 2 images in case of 4 or more and so on. Hev you identified the issue yet? – Shikhar varshney Dec 30 '15 at 14:23
  • My suspicion is that the completion block is being executed asynchronously and thus the other operations can start whilst the completion block is being executed. Try adding another dependency that is executed only when the code in the completion block finishes – Daniel Galasko Jan 04 '16 at 11:48

1 Answers1

2

A few thoughts...

1) the progress update block. This doesn't tell you which operations completed; only the count of them, so I am suspicious that they might not be completing in the order the code thinks they are all the time....

2) the completion block takes an array of operations.... I wonder if this gets called once with all of the operations, or multiple times with arrays of the operations that completed? You could look at the array length to see. If it does get called for subsets of operations, this would be the place to remove the assets that have already been uploaded and do cleanup work, since you know which operations finished.

3) I would set the upload progress block before adding the operations to the queue; just for safety.

4) I couldn't find documentation for the batchOperation method, and wondered if it has been removed from a more recent version of AFNetworking - and if so - maybe it's buggy or not good API? I'd be tempted to create my own operations in a loop for clarity; and then do a little state management to check on the status of the batch and handle that appropriately.

5) You say one image is always left out.... is it stable - always the first or last? Does it behave the same way on the sim vs on a cell network or simulated slow / unreliable connection??

Fiid
  • 1,852
  • 11
  • 22
  • 1. it tells which operation is completed :- currentUploadIndex is the variable. 2. The completion block always executes for once only when all the operations are completed. 3. Where should I keep the progress block in the code as suggested by you? 4. Can you please show a little code for your approach. 5. it is very random but behaves same on any kind of cellular network. Please suggest a way to get out of all this. – Shikhar varshney Jan 07 '16 at 07:54