在iOS中使用POST上传图片:NSData和上传

37

我一直在查阅有关在iOS中通过POST上传图像的许多帖子。尽管这个主题上有大量的信息,但我仍然无法正确上传从我的iPhone模拟器照片库中获取的JPEG数据。

一旦数据到达服务器,它只是一个巨大的十六进制字符串。难道NSData不应该只是一个字节流吗?我不明白所有这些十六进制的事情,也不知道为什么这段代码似乎对其他人都有效。

下面是相关的代码:

-(void)uploadWithUserLocationString:(NSString*)userLocation{
NSString *urlString = @"http://some.url.com/post";

// set up the form keys and values (revise using 1 NSDictionary at some point - neater than 2 arrays)
NSArray *keys = [[NSArray alloc] initWithObjects:@"auth",@"text",@"location",nil];
NSArray *vals = [[NSArray alloc] initWithObjects:self.authToken,self.textBox.text,userLocation,nil];

// set up the request object
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:@"POST"];

//Add content-type to Header. Need to use a string boundary for data uploading.
NSString *boundary = [NSString stringWithString:@"0xKhTmLbOuNdArY"];
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[request addValue:contentType forHTTPHeaderField: @"Content-Type"];

//create the post body
NSMutableData *body = [NSMutableData data];
[body appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] dataUsingEncoding:NSASCIIStringEncoding]];

//add (key,value) pairs (no idea why all the \r's and \n's are necessary ... but everyone seems to have them)
for (int i=0; i<[keys count]; i++) {
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",[keys objectAtIndex:i]] dataUsingEncoding:NSASCIIStringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"%@",[vals objectAtIndex:i]] dataUsingEncoding:NSASCIIStringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSASCIIStringEncoding]];
}
[body appendData:[[NSString stringWithString:@"Content-Disposition: form-data; name=\"image\"\r\n"] dataUsingEncoding:NSASCIIStringEncoding]];
[body appendData:[[NSString stringWithString:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSASCIIStringEncoding]];
[body appendData:[NSData dataWithData:self.imageData]];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSASCIIStringEncoding]];

// set the body of the post to the reqeust
[request setHTTPBody:body];

// make the connection to the web
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];

NSLog(returnString);
[keys release];
[vals release];
}
感谢您花费时间。
5个回答

39

这段代码在我的应用程序中可以工作。如果你没有使用 ARC,你需要修改代码来释放任何已分配的内存。

// create request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];                                    
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setHTTPShouldHandleCookies:NO];
[request setTimeoutInterval:30];
[request setHTTPMethod:@"POST"];

// set Content-Type in HTTP header
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request setValue:contentType forHTTPHeaderField: @"Content-Type"];

// post body
NSMutableData *body = [NSMutableData data];

// add params (all params are strings)
for (NSString *param in _params) {
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", param] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"%@\r\n", [_params objectForKey:param]] dataUsingEncoding:NSUTF8StringEncoding]];
}

// add image data
NSData *imageData = UIImageJPEGRepresentation(imageToPost, 1.0);
if (imageData) {
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"image.jpg\"\r\n", FileParamConstant] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithString:@"Content-Type: image/jpeg\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:imageData];
    [body appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}

[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

// setting the body of the post to the reqeust
[request setHTTPBody:body];

// set URL
[request setURL:requestURL];

3
我想在这里对这段代码添加一条评论。回车符和换行符以及额外的破折号对于这个过程的工作方式都是至关重要的。你必须将双重CR/LF放在正确的位置,并将额外的破折号放在正确的位置。我曾经认为这只是多余的东西,但它确实非常关键。XJone的代码帮助我解决了问题,我漏掉了一个可怜的CR/LF,导致上传失败。给你加一分,先生,也感谢这个问题。 - TJ Asher
1
我不确定。我没有编写服务器端代码,我正在发布到专有API。但是,服务器上的命名对客户端代码没有影响。 - XJones
我正在做类似的事情——将UIImage数据附加到NSString中,但这会导致内存问题。如果我有很多图片,甚至只是几张高分辨率的图片,将图像数据本身附加到不断增长的NSString中会引发内存警告,并最终导致应用程序崩溃。 - Matthew Quiros
我被这个问题困扰了一天。非常感谢。我原本使用MKNetworkKit,但不得不转换为NSURLRequest :) - Zeeshan
在我的应用程序中使用上述代码,在执行“[body appendData:imageData];”后,调试时我注意到“body”为nil。在appendData:imageData的上一行,我能够看到“body”的某些值。我无法确定出了什么问题? - Satyam
显示剩余14条评论

9

我希望这段代码能够帮助其他人...

//create request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];

//Set Params
[request setHTTPShouldHandleCookies:NO];
[request setTimeoutInterval:60];
[request setHTTPMethod:@"POST"];

//Create boundary, it can be anything
NSString *boundary = @"------VohpleBoundary4QuqLuM1cE5lMwCy";

// set Content-Type in HTTP header
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request setValue:contentType forHTTPHeaderField: @"Content-Type"];

// post body
NSMutableData *body = [NSMutableData data];

//Populate a dictionary with all the regular values you would like to send.
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];

[parameters setValue:param1 forKey:@"param1-name"];

[parameters setValue:param2 forKey:@"param2-name"];

[parameters setValue:param3 forKey:@"param3-name"];


// add params (all params are strings)
for (NSString *param in parameters) {
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", param] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"%@\r\n", [parameters objectForKey:param]] dataUsingEncoding:NSUTF8StringEncoding]];
}

NSString *FileParamConstant = @"imageParamName";

NSData *imageData = UIImageJPEGRepresentation(imageObject, 1);

//Assuming data is not nil we add this to the multipart form
if (imageData)
{
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"image.jpg\"\r\n", FileParamConstant] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"Content-Type:image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:imageData];
    [body appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}

//Close off the request with the boundary
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

// setting the body of the post to the request
[request setHTTPBody:body];

// set URL
[request setURL:[NSURL URLWithString:baseUrl]];

[NSURLConnection sendAsynchronousRequest:request
                                   queue:[NSOperationQueue mainQueue]
                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

                           NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;

                           if ([httpResponse statusCode] == 200) {

                               NSLog(@"success");
                           }

                       }];

如果我有图像路径 http://dev-demo.info.bh-in-15.webhostbox.net/dv/xyz/ulpoad/post/imageName,那么我应该把它放在上面的代码中的哪里? - Abhi
@Abhi 请将以下代码替换:NSData *imageData = UIImageJPEGRepresentation(imageObject, 1)。改为从路径加载图像并转换为NSData。 - Shaik Riyaz
抱歉,我无法提供评论或回答中的翻译。但是,如果您能提供要翻译的具体内容,我将尽力为您提供准确、流畅的翻译。 - Abhi
@Abhi NSData *imageData = [NSData dataWithContentOfFile:filePath]; 请提供您的文件路径。 - Shaik Riyaz

5

请查看苹果的SimpleURLConnections示例项目。您可以在那里使用PostController并进行一些修改。

然而,这并不是很简单。与上面的解决方案不同之处在于,苹果正在使用流将文件流式传输到服务器。这比在上传期间保留编码图像数据要更节省内存。但它也更加复杂。


1

我看到您在一个请求中发送了多个文件,是吗?

根据HTTP规范,您应该在嵌入multipart/form-data时使用multipart/mixed内容类型。

上面链接的示例:

Content-type: multipart/form-data, boundary=AaB03x

--AaB03x
content-disposition: form-data; name="field1"

 Joe Blow
 --AaB03x
 content-disposition: form-data; name="pics"
 Content-type: multipart/mixed, boundary=BbC04y

 --BbC04y
  Content-disposition: attachment; filename="file1.txt"

你知道如何从PHP中获取每个数据。 - Johnykutty

1
NSData *imgData = UIImagePNGRepresentation(imgUser.image);

NSString *str=[NSString stringWithFormat:@"%@upload_image",appDelegate.strRoot];
NSString *urlString = [NSString stringWithFormat:@"%@",str];

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:@"POST"];
NSMutableData *body = [NSMutableData data];
NSString *boundary = @"---------------------------14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[request addValue:contentType forHTTPHeaderField: @"Content-Type"];

[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"a.jpg\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[NSData dataWithData:imgData]];
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];

//  parameter UserId

[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"userid\"\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];

[body appendData:[appDelegate.strUserID dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];


// close form
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];


// setting the body of the post to the request 
[request setHTTPBody:body];


NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
// NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
NSDictionary *dict=[NSJSONSerialization JSONObjectWithData:returnData options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"%@",dict);

非常感谢您的代码!其他片段都无法正常工作,而您的代码之所以与众不同,是因为您使用了边界关闭表单。 - Alberto M

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