10

I can't understand why this is so hard. All the tutorials and articles online seem to be talking about the 1.0 api, which is pretty useless.

I've tried a few different ways and get different results. What am I doing wrong?

  1. upload task - this seems to not be using a multipart form, wtf?

    NSMutableURLRequest *request = [self.manager.requestSerializer multipartFormRequestWithMethod:@"POST"
                                                                                      URLString:[[NSURL URLWithString:url relativeToURL:[NSURL URLWithString:ApiBaseUrl]] absoluteString]
                                                                                     parameters:@{}
                                                                      constructingBodyWithBlock:nil];
    
    NSProgress *progress;
    NSURLSessionUploadTask *task = [self.manager uploadTaskWithRequest:request
                                                            fromData:data
                                                            progress:&progress
                                                   completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
                                                     if (error) {
                                                       NSLog(@"[error description] = %@", [error description]);
                                                     } else {
                                                       NSLog(@"success!");
                                                     }
                                                   }];
    
    [task resume];
    
  2. post with a block - this seems not to attach anything

    [self.manager POST:url
               parameters:@{}
    constructingBodyWithBlock:^(id <AFMultipartFormData> formData) {
        [formData appendPartWithFileData:data
                                    name:@"post[picture]"
                                fileName:@"picture.jpg"
                                mimeType:@"image/jpeg"];
    }
                  success:^(NSURLSessionDataTask *task, id response) {
                    NSLog(@"Success");
                  }
                  failure:^(NSURLSessionDataTask *task, NSError *error) {
                    NSLog(@"Error: %@", error);
                  }];
    
  3. simple post - this seems to almost work...but not

    [self.manager POST:url
            parameters:@{@"post[picture][]":data}
               success:^(NSURLSessionDataTask *task, id response) {
                 NSLog(@"Success");
               }
               failure:^(NSURLSessionDataTask *task, NSError *error) {
                 NSLog(@"Error: %@", error);
               }];
    

I would love 1 to work, but I'm not sure why it doesn't.

Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
Jeremy Lightsmith
  • 256
  • 1
  • 3
  • 8
  • When you use an `NSURLSessionUploadTask` in step 1, where are you getting the data from ? I'm doing something similar (though not a multipart POST) by first building the request, and then using the `request.HTTPBody` as data. – Bart Vandendriessche Oct 17 '13 at 13:52
  • have you been able to resolve this issue .. i am having same issue .. i need to send parameters with image too .. tried all three possible ways .. you mentiond .. but no one is working ... no sample is provided by afnetworking too ... – YogiAR Nov 23 '13 at 12:23
  • have you resolved it. I have similar question can you help me http://stackoverflow.com/questions/22180367/afnetworking-2-2-0-upload-image-on-server-issues – Matrosov Oleksandr Mar 04 '14 at 19:11

2 Answers2

14

For a properly formed "multipart/form-data" body, you need to use use the body construction block while creating the request. Otherwise the upload task is using the raw data as the body. For example, in your AFHTTPSessionManager subclass:

NSString *urlString = [[NSURL URLWithString:kPhotoUploadPath relativeToURL:self.baseURL] absoluteString];
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:urlString parameters:params constructingBodyWithBlock:^(id <AFMultipartFormData> formData) {
    [formData appendPartWithFileData:photo.data name:@"photo" fileName:@"photo.jpg" mimeType:@"image/jpeg"];
}];

NSURLSessionUploadTask *task = [self uploadTaskWithStreamedRequest:request progress:progress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
    if (error) {
        if (failure) failure(error);
    } else {
        if (success) success(responseObject);
    }
}];
[task resume];

Or, if you don't need to track upload progress, you can simply use:

[self POST:kPhotoUploadPath parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    [formData appendPartWithFileData:photo.data name:@"photo" fileName:@"photo.jpg" mimeType:@"image/jpeg"];
} success:^(NSURLSessionDataTask *task, id responseObject) {
    if (success) success(responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    if (failure) failure(error);
}];
Ray Lillywhite
  • 746
  • 5
  • 12
  • I'm afraid the first answer is incorrect, as `uploadTaskWithStreamedRequest:` is for *streaming* requests, i.e. through a persistent URL connection. – jpsim Oct 29 '13 at 20:49
  • I don't think that's what 'streamed' means in this context. It's likely just referring to the fact that it's a multi-part form request: http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 My understanding is that the request body is streamed, but the connection is not persistent and is closed once the request is completed. This is the standard request type for uploading files. `uploadTaskWithStreamedRequest:` is also used by AFNetworkings `...constructingBodyWithBlock:...` methods. – Ray Lillywhite Jan 07 '14 at 02:14
  • @RayLillywhite but when I check my photo.data it is nil, can you look in my similar question http://stackoverflow.com/questions/22180367/afnetworking-2-2-0-upload-image-on-server-issues – Matrosov Oleksandr Mar 04 '14 at 19:10
5

What Ray Lillywhite describes works perfectly fine (I would've made a comment on his post, but my reputation is too low).

  1. Get the correct version of AFNetworking, containing this fix for updating progress when using multipart requests. At the moment of writing, that version is HEAD.
  2. Create a NSMutableURLRequest with the help of multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:.
    • Build your form data with the help of one of the appendPartWith... methods.
  3. Get a (upload) data task by calling the right uploadTaskWith... method. You NEED to use uploadTaskWithStreamedRequest:progress:completionHandler: if you want to use the NSProgress input parameter.
Johan Forssell
  • 333
  • 4
  • 6