Search code examples
objective-cafnetworkingafnetworking-3

AFNetworking 3.0 how to upload file with additional data?


How do you implement an upload file from disk with additional information (in my case caption & channel_id) using AFNetworking 3.0 uploadTaskWithRequest:fromFile:progress:completionHandler to take advantage of the new background session uploading?

I'm open to alternative solution if it works with AFNetworking and the background file uploading.

Here's what I've tried which the server responds with 400 bad request or 500 internal processing error (doing a multipart form post in cURL works):

@property (nonatomic, strong) AFURLSessionManager *uploadSession;

// Configured uploadSession as backgroundSession configuration with couple of headers

- (void)uploadMediafileFilename:(NSString *)filename
               filenameWithPath:(NSString *)filenameWithPath
                       progress:(void ( ^ ) ( NSProgress *uploadProgress ))progress
                        caption:(NSString *)caption
                        channel:(NSString *)channelId
              completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionBlock {

// ---------
// Create Request Method #1

   NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@/", kAPIBaseURL, kAPIMediafilesURL]]];

// Tried dictionary to JSON in body. Also tried a params string version.

   NSDictionary *dict = @{ @"channel_id" : channelId,
                            @"mediafile" : @{ @"filename" : filename, @"caption" : caption } };

   request.HTTPBody = [NSKeyedArchiver archivedDataWithRootObject:dict];
   request.HTTPMethod = @"POST";


// ---- OR ----
// Create Request Method #2

   NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:[NSString stringWithFormat:@"%@%@", kAPIBaseURL, kAPIMediafilesURL] parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        [formData appendPartWithFormData:[caption dataUsingEncoding:NSUTF8StringEncoding] name:@"mediafile[caption]"];
        [formData appendPartWithFormData:[channelId dataUsingEncoding:NSUTF8StringEncoding] name:@"channel_id"];
        [formData appendPartWithFileURL:fileURL name:@"mediafile[filename]" fileName:filename mimeType:@"image/jpeg" error:nil];
    } error:nil];

// ----------

   NSURL *fileURL = [NSURL fileURLWithPath:filenameWithPath];
  [request setValue:[self token] forHTTPHeaderField:kTokenHeaderField];


// Tried using uploadTaskWithRequest - prefer due to its background capabilities. Also tried uploadTaskWithStreamedRequest.

  NSURLSessionUploadTask *uploadTask = [self.uploadSession uploadTaskWithRequest:request fromFile:fileURL progress:progress completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {

        if (error) {
            NSLog(@"%@", [response description]);
        }

        if (completionBlock) {
            completionBlock(response, responseObject, error);
        }
    }];
    [uploadTask resume];
}

Solution

  • I got this to work using the following:

    1. Configure the AFURLSessionManager *uploadSession as a defaultSessionConfiguration.
    2. Setup the NSMutableURLRequest using method #2 above.
    3. Use AFNetworking's uploadTaskWithStreamedRequest:progress:completionHandler:

    Bottomline: backgroundSessionConfiguration does not work with streamed multi-part form and I could not get additional data to be included with uploadTaskWithRequest:fromFile:.