Afnetworking 2.0 Track File Upload Progress

AFNetworking 2.0 track file upload progress

The interface of AFHTTPSession doesn't provide a method to set a progress block. Instead, you'll have to do the following:

// 1. Create `AFHTTPRequestSerializer` which will create your request.
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];

// 2. Create an `NSMutableURLRequest`.
NSMutableURLRequest *request =
[serializer multipartFormRequestWithMethod:@"POST" URLString:@"http://www.myurl.com"
parameters:dataToPost
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:imageData
name:@"attachment"
fileName:@"myimage.jpg"
mimeType:@"image/jpeg"];
}];

// 3. Create and use `AFHTTPRequestOperationManager` to create an `AFHTTPRequestOperation` from the `NSMutableURLRequest` that we just created.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
AFHTTPRequestOperation *operation =
[manager HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"Success %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Failure %@", error.description);
}];

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

// 5. Begin!
[operation start];

In addition, you don't have to read the image via UIImage and then compress it again using JPEG to get an NSData. Just use +[NSData dataWithContentsOfFile:] to read the file directly from your bundle.

AFNetworking 2.0 + UIProgressView how to correctly display progress for an upload session

Googleing, githubbing, I've found the answer, essentially this is a "feature" of NSURLSession, when you create a session with uploadstream the task takes over on your setted header. Since in a stream there is not a really content size, if we set something the task will rewrite it.

Fortunately NSURLSession has two request properties currentRequest (the overriden one) and the originalRequest. The original request will continue to keep our Content-Length.

I made few mods to the UIProgressView+AFNetworking category to make it works using the set content length, it would be better another check to see if there is a stream.

- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(__unused NSDictionary *)change
context:(void *)context
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {

//upload content length issue
// if ([object countOfBytesExpectedToSend] > 0) {
NSInteger byteToSend = [[[object originalRequest] valueForHTTPHeaderField:@"Content-Length"] integerValue];
if (byteToSend){
dispatch_async(dispatch_get_main_queue(), ^{
[self setProgress:[object countOfBytesSent] / (byteToSend * 1.0f) animated:self.af_uploadProgressAnimated];
// [self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated];
});
}
}

if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
if ([object countOfBytesExpectedToReceive] > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
[self setProgress:[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f) animated:self.af_downloadProgressAnimated];
});
}
}

if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) {
if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) {
@try {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))];

if (context == AFTaskCountOfBytesSentContext) {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
}

if (context == AFTaskCountOfBytesReceivedContext) {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
}
}
@catch (NSException * __unused exception) {}
}
}
}
#endif
}

AFNetworking Upload multiple files in sequence

As I understand, you want to upload image one by one. After first image is uploaded, start upload next image.

In my opinion, you can use recursive in this case. Take a look at my code below.

// Use recursive to upload an array items
- (void)startUploadItems:(NSMutableArray*)items {
if (items.count < 1) {
return;
}

[self startUploadItem:items[0] completion:^(BOOL success) {
[items removeObjectAtIndex:0];

[self startUploadItems:items];
}];
}

// Use to upload a single item.
- (void)startUploadItem:(id)item completion:(void(^)(BOOL success))completion {
NSLog(@"item %@", item);
NSData *imageData = UIImagePNGRepresentation(item);

NSString *urlUpload = @"https://domain/api/wp-json/wp/v2/media?access_token=";
urlUpload = [urlUpload stringByAppendingString:[Lockbox unarchiveObjectForKey:@"access_token"]];

MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeAnnularDeterminate;
hud.label.text = @"Uploaded photo";

NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:urlUpload parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:imageData name:@"file" fileName:@"filename.png" mimeType:@"image/png"];
} error:nil];

AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {

dispatch_async(dispatch_get_main_queue(), ^{

hud.progress = uploadProgress.fractionCompleted;
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
[hud hideAnimated:YES];

if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);

if ([responseObject objectForKey:@"id"] == nil ||
[[responseObject objectForKey:@"id"] isEqual:[NSNull null]] ||
[responseObject objectForKey:@"id"] == [NSNull null]) {
NSLog(@"NO ID %@", responseObject);
} else {
NSLog(@"ID: %@", [responseObject objectForKey:@"id"]);
NSLog(@"source_url: %@", [responseObject objectForKey:@"source_url"]);
}
}

if (completion) {
completion(!error);
}
}];

[uploadTask resume];
}

Usage:

[self startUploadItems:imagesArray];


Related Topics



Leave a reply



Submit