Uploading Image NSData via POST and NSURLSession

ios, nsurlsession, post

Solution

The problem was in `NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:imageData completionHandler:...]`

`NSURLSessionUploadTask` ignores the request body provided and adds the `fromData:` parameter as the request body. However, I was just providing the image data and not the properly formatted request body...

The fixed code is then `NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:body completionHandler:...]`

I'll leave my answer up in case anyone else stumbles across this problem again.

Problem

I'm trying to upload a single UIImage to a server, and everything seems to be okay except that the image is never uploaded. This is the code I'm using to upload the image in iOS: ``` const NSString *boundaryConstant = @"----------V2ymHFg03ehbqgZCaKO6jy"; const NSString *fileParamConstant = @"photo"; NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; [request setHTTPMethod:@"POST"]; NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundaryConstant]; [request setValue:contentType forHTTPHeaderField:@"Content-Type"]; NSMutableData *body = [NSMutableData data]; ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; [library assetForURL:info[UIImagePickerControllerReferenceURL] resultBlock:^(ALAsset *asset) { ALAssetRepresentation *representation = [asset defaultRepresentation]; // get byte size of image long long size = [representation size]; unsigned char *bytes = malloc(size); // read image data into bytes array [representation getBytes:bytes fromOffset:0 length:size error:nil]; NSData *imageData = [NSData dataWithBytesNoCopy:bytes length:size freeWhenDone:YES]; if (imageData) { [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fileParamConstant, filename] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n",[SWNetworkController contentTypeForImageData:imageData]] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:imageData]; [body appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; } [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]]; NSString *postLength = [NSString stringWithFormat:@"%zu", [body length]]; [request setValue:postLength forHTTPHeaderField:@"Content-Length"]; [request setHTTPBody:body]; NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:imageData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"STRING %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); NSLog(@"%@", response); NSLog(@"%@", error); }]; [uploadTask resume]; } failureBlock:^(NSError *error) { NSLog(@"Image error:\n%@",error); }]; ``` The server responds with a `200` OK status and no error, except the image is not received and nothing is returned from the server (this is expected if no image is uploaded). Here is the server-side code: ``` <? $allowedExts = array("gif", "jpeg", "jpg", "png"); $temp = explode(".", $_FILES["photo"]["name"]); $extension = end($temp); error_log(print_r($_FILES, true)); if ((($_FILES["photo"]["type"] == "image/gif") || ($_FILES["photo"]["type"] == "image/jpeg") || ($_FILES["photo"]["type"] == "image/jpg") || ($_FILES["photo"]["type"] == "image/pjpeg") || ($_FILES["photo"]["type"] == "image/x-png") || ($_FILES["photo"]["type"] == "image/png")) && ($_FILES["photo"]["size"] < 20000000) && in_array($extension, $allowedExts)) { if ($_FILES["photo"]["error"] == 0) { $filename = sha1($_FILES['photo']['name'] . uniqid("",true)); if (move_uploaded_file($_FILES['photo']['tmp_name'], 'pic/' . $filename . "." . $extension)) { // do stuff with the saved image here } } } ?> ``` A normal request (via the web interface) logs the following: ``` Array ( [photo] => Array ( [name] => BoPzSyRIgAAe1h6.jpg-large.jpeg [type] => image/jpeg [tmp_name] => /var/tmp/phpjScXQB [error] => 0 [size] => 67900 ) ) ``` Meanwhile, the request sent from iOS looks as follows: ``` Array ( ) ``` For the life of me, I can't figure out what's going wrong... Any ideas? Thanks

Original source