使用AFNetworking进行POST方式的JPEG上传

9

我无论如何都想不出为什么在使用AFNetworking时这个功能不能正常工作。但是在使用ASIHTTP时它是可以正常工作的。这对我来说都很新颖,但是我无法弄清楚为什么文件不再从$_FILES传输到服务器的硬盘上了。以下是iOS代码:

- (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];    



}

我曾经这样做:

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

以下是我的 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');
            }
?>

一些注释:你应该使用!empty($_FILES)而不是isset($_FILES),并且$_FILES['filename']['tmp_name']将返回上传文件的完整路径,因此move_uploaded_file将无法工作。如果你在php文件顶部添加了error_reporting(E_ALL),那么你就会看到错误信息... - Lawrence Cherone
谢谢!我已经将isset更改为!empty,并打开了error_reporting。但这是从我的iPhone到服务器的。我不知道应该在哪里看到错误。至于move_uploaded_files语法,我直接从php手册http://php.net/manual/en/function.move-uploaded-file.php中提取了它。 - Will Larche
我修复了 PHP 代码并在我的应用程序的旧 ASIHTTPFormRequest 版本上运行它。它仍然可以正常工作。问题肯定出在这里的 AFNetworking 实现上。 - Will Larche
3个回答

13

尝试使用以下代码片段:

    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]; 

但如果有一种方法可以在不警告的情况下使用它呢? - jeswang

0

我不太熟悉ASI在使用setPostValue:forKey:时的具体作用,但你可能需要单独发送一个name参数来上传图片。

客户端或服务器日志记录了什么具体信息?进度块有没有被记录下来?

还有几个要点:

  • 最后只需要执行[operation start]即可,无需为此创建操作队列。
  • 为了方便日志记录,可以在operation上设置completionBlock,并使用NSLog记录响应或其他类似的内容。
  • 你可能想要创建一个AFHTTPClient基类,通过类方法返回单例实例。就像AFNetworking示例应用程序中的Gowalla API客户端一样。该客户端可以管理所有网络请求的单个操作队列。

我已经调整了上面的代码,使其最新。验证了PHP的工作情况。我正在尝试完成块,但它不允许我记录operation.responsString。我应该在块中放什么? - Will Larche
啊!我让完成块工作了。它给了我“不行”的提示,这意味着它们至少是相互通信的。但我得到了400响应。所以代码说要么$_FILES为空,要么$_POST['name']未设置。我还添加了@"name" appendPartWithFormData。你可以在上面看到最近的两个。所以这个表单数据还没有被正确地发送。 - Will Larche
更新到2.0版本后,AFHTTPClient已经不存在了,使用Igor Fedorchuk的代码会出现警告,有没有新的API可以使用? - jeswang

0

我已经想到了一个使用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];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接