iOS的http POST请求(包含数据和图片):无法上传图片

4

我使用以下代码将图像和数据推送到服务器。数据已发送,但服务器未接收到图像。请问下面的代码是否有错误:

NSString *urlString = [[NSString alloc]initWithString:[NSString stringWithFormat:@"%@action=savesign",MainURL]];

// set up the form keys and values (revise using 1 NSDictionary at some point - neater than 2 arrays)
NSArray *keys = [[NSArray alloc] initWithObjects:@"user",@"poll",nil];
NSArray *vals = [[NSArray alloc] initWithObjects:user,pollid,nil];

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

//Add content-type to Header. Need to use a string boundary for data uploading.
NSString *boundary = @"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:[@"Content-Disposition: form-data; name=\"image\"\r\n" dataUsingEncoding:NSASCIIStringEncoding]];
[body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSASCIIStringEncoding]];
[body appendData:[NSData dataWithContentsOfFile:pngFilePath]];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSASCIIStringEncoding]];


NSData *imageData = UIImagePNGRepresentation(_Signfield.image);
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"myPngFile.png\"\r\n", _Signfield.image] 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]];

// 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);

这是服务器返回的响应。
{"status":"failure","error":[],"user":0}

我登录后查看数据发现有数据但没有图像。

1个回答

15

当你通过HTTP POST执行文件上传时,传输的数据看起来像这样:

POST /upload HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, compress
Content-Length: 17918
Content-Type: multipart/form-data; boundary=0xKhTmLbOuNdArY
Host: example.com
User-Agent: HTTPie/0.7.2

--0xKhTmLbOuNdArY
Content-Disposition: form-data; name="user"

Diphin-Das
--0xKhTmLbOuNdArY
Content-Disposition: form-data; name="poll"

1
--0xKhTmLbOuNdArY
Content-Disposition: form-data; name="image"; filename="myPNGFile.png"
Content-Type: image/png

[Binary PNG image data not shown]
--0xKhTmLbOuNdArY--

前七行是HTTP头,用于描述向服务器发送的请求,包括:

  • HTTP请求方法(POST
  • 所请求的资源(/upload
  • HTTP协议版本号(HTTP/1.1
  • 请求体的长度(Content-Length: 17918
  • 请求体中包含的数据类型(Content-Type: multipart/form-data; boundary=0xKhTmLbOuNdArY

最后一个很有趣。通过将内容类型设置为multipart/form-data ,我们可以在请求体中包含不同类型的数据。 boundary告诉服务器如何在请求体中分隔每个表单值。

请求体中的表单值使用简单的结构进行描述:

--[boundary marker]
Content-Disposition: form-data; name="[parameter name]"
Content-Type: [parameter value MIME type]

[parameter value]

如果参数值是字母数字,则Content-Type头是可选的,但对于其他数据类型(图像、视频、文档等),则需要Content-Type头。请求正文的结尾由终止边界标记表示,这是一个带有双破折号后缀的标准边界标记,例如--0xKhTmLbOuNdArY--。换行符(\r\n)用于分隔内容部分的各个元素。

在多部分POST请求的表单值中可能还有其他元素。如果您有兴趣,可以阅读RFC 2388了解它们。

为了从Objective-C上传文件,您需要根据上述规范制作请求正文。 我从您的问题中获取了代码并进行了重构以使其正确运行,并在此过程中添加了一些说明性注释。

NSDictionary *params = @{ @"user": user, @"poll": pollid };
NSData *imageData = UIImagePNGRepresentation(_Signfield.image);

NSString *urlString = [[NSString alloc]initWithString:[NSString stringWithFormat:@"%@action=savesign",MainURL]];

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:@"POST"];

NSString *boundary = @"0xKhTmLbOuNdArY";
NSString *kNewLine = @"\r\n";

// Note that setValue is used so as to override any existing Content-Type header.
// addValue appends to the Content-Type header
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[request setValue:contentType forHTTPHeaderField: @"Content-Type"];

NSMutableData *body = [NSMutableData data];

// Add the parameters from the dictionary to the request body
for (NSString *name in params.allKeys) {
    NSData *value = [[NSString stringWithFormat:@"%@", params[name]] dataUsingEncoding:NSUTF8StringEncoding];

    [body appendData:[[NSString stringWithFormat:@"--%@%@", boundary, kNewLine] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"", name] dataUsingEncoding:NSUTF8StringEncoding]];
    // For simple data types, such as text or numbers, there's no need to set the content type
    [body appendData:[[NSString stringWithFormat:@"%@%@", kNewLine, kNewLine] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:value];
    [body appendData:[kNewLine dataUsingEncoding:NSUTF8StringEncoding]];
}

// Add the image to the request body
[body appendData:[[NSString stringWithFormat:@"--%@%@", boundary, kNewLine] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"myPngFile.png\"%@", @"image", kNewLine] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Type: image/png"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"%@%@", kNewLine, kNewLine] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:imageData];
[body appendData:[kNewLine dataUsingEncoding:NSUTF8StringEncoding]];

// Add the terminating boundary marker to signal that we're at the end of the request body
[body appendData:[[NSString stringWithFormat:@"--%@--", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

[request setHTTPBody:body];

NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];

NSLog(@"%@", returnString);

我并没有真正理解如何使用它。我在堆栈溢出上尝试了很多链接,但仍然不清楚如何使用它。 你能给我提供一个关于HTTP完整概念的链接吗? - Spidy
1
@DhipinDas 我已经重写了我的答案。希望它有所帮助。 - neilco
1
非常感谢你,neilco...我一直在处理一个大项目,这个问题困扰了我好几天... 你的代码真的有效。还有感谢你对这个概念的说明。 我很兴奋,所以我觉得你应该得到50+的奖励...... 11小时后赏金将会可用,我会授予你这个奖励... :) 再次感谢。 - Spidy
1
@DhipinDas 感谢你的慷慨奖励,非常感谢。 - neilco

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