通过多部分HTTP请求上传ios文本

时间:2013-12-02 12:35:18

标签: ios http-post nsurlconnection

我正在尝试使用iOS中的multi-part form encoding上传文字。

我做了什么

我已尝试通过ios Upload Image and Text using HTTP POST

中提供的信息

问题

我把登录服务器端,这是我从IOS App收到的内容。

key名称没问题。但是,我收到的valuenull

"443" "POST" "/api/private/json/" "content=null&scope=null&action=null&documentname=null&apikey=null"

我的代码

    NSMutableDictionary *params = [NSMutableDictionary new];
    [params setObject:@"XXXXX" forKey:@"apikey"];
    [params setObject:@"DataAPI" forKey:@"scope"];
    [params setObject:@"push" forKey:@"action"];
    [params setObject:docName forKey:@"documentName"];
    [params setObject:content forKey:@"content"];
    [params setObject:authToken forKey:@"authtoken"];

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

    // set Content-Type in HTTP header
    NSString *boundary = @"V2ymHFg03ehbqgZCaKO6jy";

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

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

    }

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


    // setting the body of the post to the reqeust
    [request setHTTPBody:body];
    NSString *myString = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding];


    NSLog(@"%@",myString);
    // set the content-length
    NSString *postLength = [NSString stringWithFormat:@"%d", [body length]];
    [request setValue:postLength forHTTPHeaderField:@"Content-Length"];

    // set URL
    [request setURL:url];


    NSURLResponse *response;
    NSData *POSTReply = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
    NSString *theReply = [[NSString alloc] initWithBytes:[POSTReply bytes] length:[POSTReply length] encoding: NSASCIIStringEncoding];
    NSLog(@"Reply: %@", theReply);

我无法理解这个问题,无论是来自服务器端还是来自我的代码。

感谢您分享您的答案。

3 个答案:

答案 0 :(得分:1)

您没有附加终止边界。

此:

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

应该是这样的:

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

修改

你实际上缺少了一些东西。您没有为数据设置内容类型,而且您缺少一些新行。

你的循环应如下所示:

for (NSString *param in params) {
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n"] 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]];
}
[body appendData:[[NSString stringWithFormat:@"\r\n\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

<强> EDIT2:

以下是正确处理相同请求的服务器端代码示例:

#!/usr/bin/env python

import web

urls = ("/", "Index")
app = web.application(urls, globals())

class Index(object):
    def POST(self):
        x = web.input()
        print x
        print x['apikey']
        print x['scope']
        return 'success'

if __name__ == "__main__":
    app.run()

输出:

<Storage {'scope': u'asd', 'apikey': u'123\r\n'}>
123

asd
127.0.0.1:60044 - - [02/Dec/2013 15:02:55] "HTTP/1.1 POST /" - 200 OK

示例控制台应用。您可以使用clang编译 - clang -framework Foundation test_post.m -o test_post

#import <Foundation/Foundation.h>

@interface Uploader : NSObject <NSURLConnectionDataDelegate>

@property (nonatomic, strong) NSDictionary *parameters;
@property (nonatomic, strong) NSURLConnection *connection;

@end

@implementation Uploader

- (id)init {
    if (self = [super init]) {
    }
    return self;
}

- (NSMutableURLRequest*)createRequest {
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:@"http://localhost:8080/"]];
    [request setHTTPMethod:@"POST"];
    NSString *boundary = @"0xB0un9ArY";
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
    [request addValue:contentType forHTTPHeaderField:@"Content-Type"];
    [request setValue:@"1.0" forHTTPHeaderField:@"MIME-Version"];
    [request setValue:@"keep-alive" forHTTPHeaderField:@"Connection"];

    NSMutableData *body = [NSMutableData data];

    for (NSString *key in [self.parameters allKeys]) {
        [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"%@\r\n", (self.parameters)[key]] dataUsingEncoding:NSUTF8StringEncoding]];
    }
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [request setHTTPBody:body];

    return request;
}

- (void)upload {
    NSMutableURLRequest *request = [self createRequest];
    self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    if (self.connection) [self.connection start];
}

- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response {NSLog(@"connection:didReceiveResponse: %@", response);} 
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {NSLog(@"connection: didReceiveData: %@", data);} 
- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error {NSLog(@"connection: didFailWithError: %@", error);}
- (void)connectionDidFinishLoading:(NSURLConnection*)connection { NSLog(@"connectionDidFinishLoading:"); }
@end

int main(int argc, char *argv[])
{
    @autoreleasepool {
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

    Uploader *uploader = [[Uploader alloc] init];
    uploader.parameters = @{@"apikey": @"123", @"scope": @"asd"};
    [uploader upload];

    [runLoop run];
    }
    return 0;
}

答案 1 :(得分:1)

这应该解决它:

而不是:

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

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

(终止CRLF是可选的)

请注意如果你写这个:

for (NSString *param in params) {
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n"] 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]];
}
[body appendData:[[NSString stringWithFormat:@"\r\n\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

在边界定界符之前的所有内容,例如\r\n--<boundary>在概念上属于数据,例如到参数值。

同样,“破折号边界”(CRLF)的前面--<boundary>属于分隔符。

从概念上讲,第一个分隔符也必须以CRLF开头:

`CRLF--<boundary>`

然而,似乎NSURLConnection已经在消息头之后放置了一个CRLF 。所以我们最终得到了这个:

Content-Type: multipart/form-data; boundary="xyz"CRLF
<other header>CRLF
CRLF             <-- added by NSURLConnection, which is debatable if this is correct
... multipart body starts here

服务器应忽略在标题之后的任何字节多部分开始之前的。这种垃圾称为preamble

Content-Type: multipart/form-data; boundary="xyz"CRLF
<other header>CRLF
<preamble bytes>CRLF--xyz
                ^~~~~~~~~ delimiter
....
例如,

preamble可能为空,也可能包含任意数量的CRLF。

body-part由MIME标题和部分正文数据组成,位于边界之后,但严格来说不是立即:

<preamble-bytes>CRLF--xyz<transport-padding>CRLF<body-part> 
                ^~~~~~~~~ delimiter             ^~~~~~~~~~~ MIME headers and data

其中<transport-padding>是任意数量的空格或水平制表符,也就是说,它可能为空。

这一切都意味着我们应该能够使用以下行作为非终止定界符(对于第一个和任何后续的非终止边界定界符)加上CRLF:

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

然后紧随其后的body-part(从标题开始,然后是数据):

[body appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"%@", (self.parameters)[key]] dataUsingEncoding:NSUTF8StringEncoding]];

这四行可以在每个参数的循环中使用。

可以按如下方式添加终止分隔符:

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

终止CRLF是可选的。最后一个CRLF之后的任何字节都属于epilogue,服务器将忽略该字节。

见:RCF 2046 http://www.ietf.org/rfc/rfc2046.txt

答案 2 :(得分:0)

NSURL *url = [NSURL URLWithString:@"http://210.7.64.98/360videos/webservices/devicetoken.php"];
NSDictionary *postDict = [NSDictionary dictionaryWithObjectsAndKeys:devToken1, @"token",
                          @"iphone", @"device_type", nil];
NSData *postData = [self encodeDictionary:postDict];

// Create the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setValue:[NSString stringWithFormat:@"%d", postData.length] forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];




dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Peform the request
    NSURLResponse *response;
    NSError *error = nil;
    NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
                                                 returningResponse:&response
                                                             error:&error];
    if (error) {
        // Deal with your error
        if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
            NSLog(@"HTTP Error: %d %@", httpResponse.statusCode, error);
            return;
        }
        NSLog(@"Error %@", error);
        return;
    }


    NSString *responeString = [[NSString alloc] initWithData:receivedData
                                                    encoding:NSUTF8StringEncoding];
    NSLog(@"token Response : %@",responeString);
});

//这里是编码方法

  • (NSData *)encodeDictionary:(NSDictionary *)dictionary { NSMutableArray * parts = [[NSMutableArray alloc] init]; for(NSString *字典中的键){     NSString * encodedValue = [[dictionary objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];     NSString * encodedKey = [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];     NSString * part = [NSString stringWithFormat:@“%@ =%@”,encodedKey,encodedValue];     [parts addObject:part]; } NSString * encodedDictionary = [parts componentsJoinedByString:@“&amp;”]; return [encodedDictionary dataUsingEncoding:NSUTF8StringEncoding]; } //可能会对你有所帮助。