1

I know this issue has been very popular on this Site, but after searching most of answers, I couldn't find out the solution. My app is to upload an image to the remote url. I tried several ways to upload and all of them successful:
(1) Use AFURLSessionManager

AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[(AFHTTPResponseSerializer*)manager.responseSerializer setAcceptableContentTypes:[NSSet setWithObjects:@"multipart/form-data", nil]];
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
    if (error) {
        NSDictionary *userInfo=[error userInfo];
        id responseError=[userInfo objectForKey:@"AFNetworkingOperationFailingURLResponseErrorKey"];
        if ([responseError isKindOfClass:[NSHTTPURLResponse class]]) {
            NSHTTPURLResponse *reponse=(NSHTTPURLResponse*)responseError;
            if (reponse.statusCode==200) {
                result(YES);
            } else {
                result(NO);
            }
        } else {
            result(NO);
        }
    } else {
        result(YES);
    }
}];
[uploadTask resume];

For this way, I used KVO to monitor progress, but no luck.

(2) Use AFHTTPRequestOperation

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    [operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
        NSLog(@"total bytes written: %lld",totalBytesWritten);
    }];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        result(YES);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        if (operation.response.statusCode==200) {
            result(YES);
        } else {
            result(NO);
        }
    }];
    [operation start];

It only shows the log "total bytes written:" only once with 100% volume of the file immediately when starting uploading.

(3) Use AFHTTPRequestOperationManager

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    AFHTTPRequestOperation *operation =
    [manager HTTPRequestOperationWithRequest:request
                                     success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                         NSLog(@"Success %@", responseObject);
                                         result(YES);
                                     } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                         if (operation.response.statusCode==200) {
                                             result(YES);


              } else {
                                         result(NO);
                                     }
                                 }];

// 4. Set the progress block of the operation.
[operation setUploadProgressBlock:^(NSUInteger __unused bytesWritten,
                                    long long totalBytesWritten,
                                    long long totalBytesExpectedToWrite) {
    double percentDone = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
    NSLog(@"progress updated(percentDone) : %f", percentDone);
}];

// 5. Begin!
[operation start];

=> Same result to the 2nd solution.

Notice that all solutions do uploading successfully but the progress not updated (NSProgress or block).

nhgrif
  • 61,578
  • 25
  • 134
  • 173
lenhhoxung
  • 2,530
  • 2
  • 30
  • 61

3 Answers3

1
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"<your upload url>" parameters:@{<parameters dict> } constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
         _STRONGREF(strongSelf);

            NSString  *name=@"file_data_name";
            [formData appendPartWithFileURL:[NSURL fileURLWithPath:path] name:name fileName:<file Path> mimeType:@"image/png" error:nil];//u can use data as well with other method
        }

    } error:nil];
    [request setTimeoutInterval:180];
    [request setValue:@"VAlue_your" forHTTPHeaderField:@"feild"];//set headers
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSProgress *progress = nil;
    NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
        if (error) {
            NSLog(@"Error: %@", error);
        } else {
            NSLog(@"%@ %@", response, responseObject);
        }
    }];

    NSLog(@"%@",progress);
    [uploadTask resume];

My macros are

#define  _WEAKREF(obj)         __weak typeof(obj)ref=obj;
#define  _STRONGREF(name)      __strong typeof(ref)name=ref;\
                               if(!name)return ;\
amar
  • 4,285
  • 8
  • 40
  • 52
0

Set an observer on your NSProgress.

Something like

[uploadTask resume];
[progress addObserver:self
                   forKeyPath:@"fractionCompleted"
                      options:NSKeyValueObservingOptionNew
                      context:NULL];

and then just implement

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"fractionCompleted"]) {
    NSProgress *progress = (NSProgress *)object;
    NSLog(@"progress = %f", progress.fractionCompleted);

} else {
    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

Don't forget to remove the observer once the upload task is complete.

NOTE: progress.fractionCompleted returns a percentage value. It's up to you to calculate it in MB for example.

Andrei Filip
  • 1,145
  • 15
  • 33
  • Thans, but I used bove code in my project (KVO-Key value observer). I have no idea why the observer doesn't fire. – lenhhoxung Jun 18 '14 at 01:08
0

Finally, I found out the reason why the progress block not called: I've uploaded a very small image that is around tens of Kb. I tried upload the file with bigger size and the progress block get called. From my experience, KVO with NSProgress doesn't work. Instead of that, we can use AFHTTPRequestOperation or AFHTTPRequestOperationManager. If you prefer do uploading manually, you can use NSURLConnection as suggested at How can I upload a photo to a server with the iPhone? together with delegate method:
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { NSLog(@"percent: %d",100*totalBytesWritten/totalBytesExpectedToWrite); }

Community
  • 1
  • 1
lenhhoxung
  • 2,530
  • 2
  • 30
  • 61
  • I uploaded a larger file as u suggested and the delegate connection:didSendBodyData is being called also the log is printed. but 1) the self.uploadProgress in the delegate is nil and so the callback block setUploadProgressBlock is not being called. 2) debugging in AFURLConnectionOperation.m depicts that self.uploadProgress which had content while setUploadProgressBlock becomes nil in delegate. – Krishna Shanbhag Oct 21 '15 at 10:22