multipart PUT request using AFNetworking
Asked Answered
T

6

8

What's the correct way to code a multipart PUT request using AFNetworking on iOS? (still Objective-C, not Swift)

I looked and seems like AFNetworking can do multipart POST but not PUT, what's the solution for that?

Thanks

Torsk answered 22/5, 2015 at 3:40 Comment(0)
S
15

You can use multipartFormRequestWithMethod to create a multipart PUT request with desired data.

For example, in AFNetworking v3.x:

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

NSError *error;
NSURLRequest *request = [manager.requestSerializer multipartFormRequestWithMethod:@"PUT" URLString:@"http://example.com/rest/api/" parameters:@{@"foo" : @"bar"} constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    NSString *value = @"qux";
    NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
    [formData appendPartWithFormData:data name:@"baz"];
} error:&error];

NSURLSessionDataTask *task = [manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
    if (error) {
        NSLog(@"%@", error);
        return;
    }

    // if you want to know what the statusCode was

    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
        NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
        NSLog(@"statusCode: %ld", statusCode);
    }

    NSLog(@"%@", responseObject);
}];
[task resume];

If AFNetworking 2.x, you can use AFHTTPRequestOperationManager:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

NSError *error;
NSURLRequest *request = [manager.requestSerializer multipartFormRequestWithMethod:@"PUT" URLString:@"http://example.com/rest/api/" parameters:@{@"foo" : @"bar"} constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    NSString *value = @"qux";
    NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
    [formData appendPartWithFormData:data name:@"baz"];
} error:&error];

AFHTTPRequestOperation *operation = [manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"%@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"%@", error);
}];

[manager.operationQueue addOperation:operation];

Having illustrated how one could create such a request, it's worth noting that servers may not be able to parse them. Notably, PHP parses multipart POST requests, but not multipart PUT requests.

Steerage answered 22/5, 2015 at 3:52 Comment(7)
Tried above code but getting folowing error { "image": [ "The submitted data was not a file. Check the encoding type on the form." ] }Chiaroscuro
Did you use appendPartWithFormData or appendPartWithFileData? It sounds like you want the latter.Steerage
@Steerage I've implemented your solution but I'm getting bytes data as responseObject. Can you address to this issue here: #40424823Snooze
@Steerage please how can I get the returned status code from the session task?Mawson
@Llg - It's part of the response object. So, check to see if it is a NSHTTPURLResponse object, and if so, grab its statusCode. See revised example above.Steerage
@Steerage can you please provide the same example for AFNetworking v3.0 but for the multipart/form-data? I'm trying to upload an image to the server but I can't get it workMawson
First, if it's a multipart/form-data, its exceeding unlikely to be PUT. It's generally POST, in which case you use the standard AFNetworking API for posting of multipart data, not the silliness above. Second, my first example is AFNetworking 3 PUT with multipart/form-data. I must not understand your question.Steerage
L
3

Here is code for Afnetworking 3.0 and Swift that worked for me. I know its old thread but might be handy for someone!

    let manager: AFHTTPSessionManager = AFHTTPSessionManager()

    let URL = "\(baseURL)\(url)"        

    let request: NSMutableURLRequest = manager.requestSerializer.multipartFormRequestWithMethod("PUT", URLString: URL, parameters: parameters as? [String : AnyObject], constructingBodyWithBlock: {(formData: AFMultipartFormData!) -> Void in
        formData.appendPartWithFileData(image!, name: "Photo", fileName: "photo.jpg", mimeType: "image/jpeg")
        }, error: nil)

    manager.dataTaskWithRequest(request) { (response, responseObject, error) -> Void in

        if((error == nil)) {
            print(responseObject)
            completionHandler(responseObject as! NSDictionary,nil)
        }
        else {
            print(error)
            completionHandler(nil,error)
        }

        print(responseObject)
        }.resume()
Locular answered 16/5, 2016 at 16:16 Comment(0)
Y
0

You can create an NSURLRequest constructed with the AFHTTPRequestSerialization's multipart form request method

NSString *url = [[NSURL URLWithString:path relativeToURL:manager.baseURL] absoluteString];
id block = ^(id<AFMultipartFormData> formData) {
    [formData appendPartWithFileData:media
                                name:@"image"
                            fileName:@"image"
                            mimeType:@"image/jpeg"];
};

NSMutableURLRequest *request = [manager.requestSerializer
                                multipartFormRequestWithMethod:@"PUT"
                                URLString:url
                                parameters:nil
                                constructingBodyWithBlock:block
                                error:nil];

[manager HTTPRequestOperationWithRequest:request success:successBlock failure:failureBlock];
Yurik answered 24/8, 2015 at 9:17 Comment(0)
M
0

I came up with a solution that can handle any supported method. This is a solution for PUT, but you can replace it with POST as well. This is a method in a category that I call on the base model class.

    - (void)updateWithArrayOfFiles:(NSArray *)arrayOfFiles forKey:(NSString *)key params:(NSDictionary *)params success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {

        id block = [self multipartFormConstructionBlockWithArayOfFiles:arrayOfFiles forKey:key failureBlock:failure];

        NSMutableURLRequest *request = [[self manager].requestSerializer
                                        multipartFormRequestWithMethod:@"PUT"
                                        URLString:self.defaultURL
                                        parameters:nil
                                        constructingBodyWithBlock:block
                                        error:nil];

       AFHTTPRequestOperation *operation = [[self manager] HTTPRequestOperationWithRequest:request success:success failure:failure];
       [operation start];
    }

    #pragma mark multipartForm constructionBlock

    - (void (^)(id <AFMultipartFormData> formData))multipartFormConstructionBlockWithArayOfFiles:(NSArray *)arrayOfFiles forKey:(NSString *)key failureBlock:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {
        id block = ^(id<AFMultipartFormData> formData) {
            int i = 0;
            // form mimeType
            for (FileWrapper *fileWrapper in arrayOfFiles) {
                NSString *mimeType = nil;
                switch (fileWrapper.fileType) {
                    case FileTypePhoto:
                        mimeType = @"image/jpeg";
                        break;
                    case FileTypeVideo:
                        mimeType = @"video/mp4";
                        break;
                    default:
                        break;
                }
                // form imageKey
                NSString *imageName = key;
                if (arrayOfFiles.count > 1)
                    // add array specificator if more than one file
                    imageName = [imageName stringByAppendingString: [NSString stringWithFormat:@"[%d]",i++]];
                // specify file name if not presented
                if (!fileWrapper.fileName)
                    fileWrapper.fileName  = [NSString stringWithFormat:@"image_%d.jpg",i];
                NSError *error = nil;

                // Make the magic happen
                [formData appendPartWithFileURL:[NSURL fileURLWithPath:fileWrapper.filePath]
                                           name:imageName
                                       fileName:fileWrapper.fileName
                                       mimeType:mimeType
                                          error:&error];
                if (error) {
                    // Handle Error
                    [ErrorManager logError:error];
                    failure(nil, error);
                }
            }
        };
        return block;
    }

Aso it uses FileWrapper Interface

    typedef NS_ENUM(NSInteger, FileType) {
        FileTypePhoto,
        FileTypeVideo,
    };


@interface FileWrapper : NSObject

@property (nonatomic, strong) NSString *filePath;
@property (nonatomic, strong) NSString *fileName;
@property (assign, nonatomic) FileType fileType;

@end
Maciemaciel answered 12/11, 2015 at 17:38 Comment(0)
P
0

For RAW Body :

NSData *data = someData; NSMutableURLRequest *requeust = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[self getURLWith:urlService]]];

[reqeust setHTTPMethod:@"PUT"];
[reqeust setHTTPBody:data];
[reqeust setValue:@"application/raw" forHTTPHeaderField:@"Content-Type"];

NSURLSessionDataTask *task = [manager uploadTaskWithRequest:requeust fromData:nil progress:^(NSProgress * _Nonnull uploadProgress) {

} completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {

}];
[task resume];
Physiological answered 25/11, 2016 at 14:37 Comment(0)
L
0

.h

+ (void)PUT:(NSString *)URLString
 parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
   progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
    success:(void (^)(NSURLResponse *response, id responseObject))success
    failure:(void (^)(NSURLResponse * response, NSError *error))failure
      error:(NSError *__autoreleasing *)requestError;

.m:

+ (void)PUT:(NSString *)URLString
 parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
    success:(void (^)(NSURLResponse * _Nonnull response, id responseObject))success
    failure:(void (^)(NSURLResponse * _Nonnull response, NSError *error))failure
error:(NSError *__autoreleasing *)requestError {

    NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer]
                                    multipartFormRequestWithMethod:@"PUT"
                                    URLString:(NSString *)URLString
                                    parameters:(NSDictionary *)parameters
                                    constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                                    error:(NSError *__autoreleasing *)requestError];
    AFURLSessionManager *manager = [AFURLSessionManager sharedManager];//[AFURLSessionManager manager]
    NSURLSessionUploadTask *uploadTask;
    uploadTask = [manager uploadTaskWithStreamedRequest:(NSURLRequest *)request
                                               progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                      completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
                                          if (error) {
                                              failure(response, error);
                                          } else {
                                              success(response, responseObject);
                                          }
                                      }];

    [uploadTask resume];
}

Just like the classic afnetworking. Put it to your net work Util :)

Lepus answered 9/2, 2018 at 1:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.