9

I can't for the life of me figure out why this doesn't work when I use AFNetworking. It worked with ASIHTTP. This is all very new to me. But I can't figure out why the files are not being transferred from $_FILES to the server's HD anymore. Here's the iOS code:

- (IBAction)uploadPressed 
{
[self.fileName resignFirstResponder];
NSURL *remoteUrl = [NSURL URLWithString:@"http://mysite.com"];

NSTimeInterval timeInterval = [NSDate timeIntervalSinceReferenceDate];
NSString *photoName=[NSString stringWithFormat:@"%lf-Photo.jpeg",timeInterval];

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

// the path to write file
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:photoName];
NSData * photoImageData = UIImageJPEGRepresentation(self.remoteImage.image, 1.0);
[photoImageData writeToFile:filePath atomically:YES];

NSLog(@"photo written to path: e%@", filePath);

AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:remoteUrl];
NSMutableURLRequest *afRequest = [httpClient multipartFormRequestWithMethod:@"POST" 
                                                                       path:@"/photos" 
                                                                 parameters:nil 
                                                  constructingBodyWithBlock:^(id <AFMultipartFormData>formData) 
                                  {
                                      [formData appendPartWithFormData:[self.fileName.text dataUsingEncoding:NSUTF8StringEncoding] 
                                                                  name:@"name"];


                                      [formData appendPartWithFileData:photoImageData 
                                                                  name:self.fileName.text 
                                                              fileName:filePath 
                                                              mimeType:@"image/jpeg"]; 
                                  }
                                  ];

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:afRequest];
[operation setUploadProgressBlock:^(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) {

    NSLog(@"Sent %d of %d bytes", totalBytesWritten, totalBytesExpectedToWrite);

}];

   [operation setCompletionBlock:^{
    NSLog(@"%@", operation.responseString); //Gives a very scary warning
}];

[operation start];    



}

I used to do this:

ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:remoteUrl];
[request setPostValue:self.fileName.text forKey:@"name"];
[request setFile:filePath forKey:@"filename"];
[request setDelegate:self];
[request startAsynchronous];

Here's my PHP:

 {
// these could be stored in a .ini file and loaded
// via parse_ini_file()... however, this will suffice
// for an example
$codes = Array(
    100 => 'Continue',
    101 => 'Switching Protocols',
    200 => 'OK',
    201 => 'Created',
    202 => 'Accepted',
    203 => 'Non-Authoritative Information',
    204 => 'No Content',
    205 => 'Reset Content',
    206 => 'Partial Content',
    300 => 'Multiple Choices',
    301 => 'Moved Permanently',
    302 => 'Found',
    303 => 'See Other',
    304 => 'Not Modified',
    305 => 'Use Proxy',
    306 => '(Unused)',
    307 => 'Temporary Redirect',
    400 => 'Bad Request',
    401 => 'Unauthorized',
    402 => 'Payment Required',
    403 => 'Forbidden',
    404 => 'Not Found',
    405 => 'Method Not Allowed',
    406 => 'Not Acceptable',
    407 => 'Proxy Authentication Required',
    408 => 'Request Timeout',
    409 => 'Conflict',
    410 => 'Gone',
    411 => 'Length Required',
    412 => 'Precondition Failed',
    413 => 'Request Entity Too Large',
    414 => 'Request-URI Too Long',
    415 => 'Unsupported Media Type',
    416 => 'Requested Range Not Satisfiable',
    417 => 'Expectation Failed',
    500 => 'Internal Server Error',
    501 => 'Not Implemented',
    502 => 'Bad Gateway',
    503 => 'Service Unavailable',
    504 => 'Gateway Timeout',
    505 => 'HTTP Version Not Supported'
);

return (isset($codes[$status])) ? $codes[$status] : '';
}

function sendResponse($status = 200, $body = '', $content_type = 'text/html')
{
$status_header = 'HTTP/1.1 ' . $status . ' ' . getStatusCodeMessage($status);
header($status_header);
header('Content-type: ' . $content_type);
echo $body;
}

if (!empty($_FILES) && isset($_POST["name"])) {
            $name = $_POST["name"];
            $tmp_name = $_FILES['filename']['tmp_name'];
            $uploads_dir = '/var/www/cnet/photos';
            move_uploaded_file($tmp_name, "$uploads_dir/$name.jpg");
            $result = array("SUCCEEDED");
            sendResponse(200, json_encode($result));
            } else {

            sendResponse(400, 'Nope');
            }
?>
Will Larche
  • 3,119
  • 7
  • 26
  • 34
  • Some comments: you should use `!empty($_FILES)` instead of `isset($_FILES)` & `$_FILES['filename']['tmp_name']` will return the full path to the uploaded file, so move_uploaded_file would not work, if you added `error_reporting(E_ALL)` to the top of your php file then you would see the error... – Lawrence Cherone Dec 19 '11 at 05:22
  • Thanks! I went ahead and made the change from isset to !empty. And turned on error_reporting. But this is from my iPhone to the server. I don't know where I should see the errors. And regarding the move_uploaded_files syntax, I pulled that straight from the php manual http://php.net/manual/en/function.move-uploaded-file.php. – Will Larche Dec 19 '11 at 06:39
  • I fixed the php code and ran it with the old ASIHTTPFormRequest version of my app. It still works like that. The problem is definitely in the AFNetworking implementation here. – Will Larche Dec 19 '11 at 19:13

3 Answers3

13

Try this snippet of code:

    NSData* sendData = [self.fileName.text dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *sendDictionary = [NSDictionary dictionaryWithObject:sendData forKey:@"name"];
    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:remoteUrl];
    NSMutableURLRequest *afRequest = [httpClient multipartFormRequestWithMethod:@"POST" 
                                                                           path:@"/photos" 
                                                                     parameters:sendDictionary 
                                                      constructingBodyWithBlock:^(id <AFMultipartFormData>formData) 
                                      {                                     
                                          [formData appendPartWithFileData:photoImageData 
                                                                      name:self.fileName.text 
                                                                  fileName:filePath 
                                                                  mimeType:@"image/jpeg"]; 
                                      }
                                      ];

    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:afRequest];
    [operation setUploadProgressBlock:^(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) {

        NSLog(@"Sent %d of %d bytes", totalBytesWritten, totalBytesExpectedToWrite);

    }];

    [operation setCompletionBlock:^{
        NSLog(@"%@", operation.responseString); //Gives a very scary warning
    }];

    [operation start]; 
Igor
  • 4,235
  • 3
  • 34
  • 32
0

I'm not really familiar with what ASI is doing with setPostValue:forKey:, but you may be missing a name parameter to be sent separately from the image upload.

What does either the client or server log, exactly? Is the progress block logging?

A few other points:

  • You can just do [operation start] at the end; no need to create an operation queue for just that.
  • To help with logging, set completionBlock on operation, with an NSLog of the response, or something like that.
  • You may want to create an AFHTTPClient base class with a class method to return a singleton instance, like the Gowalla API client in the AFNetworking example app. That client can then manage a single operation queue for all of your network requests.
mattt
  • 19,544
  • 7
  • 73
  • 84
  • I adjusted the code above to be most recent. Verified that the PHP works. I am trying to do the completion block but it won't let me log operation.responsString. What should I put in the block instead? – Will Larche Dec 20 '11 at 20:49
  • Ach! I got the completion block to work. It's giving me "Nope" which means they are at least communicating to each other. But I'm getting a 400 response. So the code says that either $_FILES is empty or $_POST['name'] is not set. I also added in the @"name" appendPartWithFormData. You can see above all the most recent of both. So this form data is not being sent just right yet. – Will Larche Dec 20 '11 at 21:06
  • After we update to 2.0, there is no AFHTTPClient and using Igor Fedorchuk's code gets an warning, any new apis? – jeswang Jan 05 '14 at 02:06
0

I've got a workaround using NSMutableURLRequest:

NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:remoteUrl];
[req setHTTPMethod:@"POST"];

NSString *contentType = [NSString stringWithFormat:@"multipart/form-data, boundary=%@", boundary];
[req setValue:contentType forHTTPHeaderField:@"Content-type"];

//adding the body:
NSMutableData *postBody = [NSMutableData data];
[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[@"Content-Disposition: form-data; name=\"name\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[name dataUsingEncoding:NSUTF8StringEncoding]];

[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[@"Content-Disposition: form-data; name=\"filename\";\r\nfilename=\"china.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[NSData dataWithData:imageData]];
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[req setHTTPBody:postBody];
Will Larche
  • 3,119
  • 7
  • 26
  • 34