【问题标题】:iOS, upload file with multipartform-dataiOS,使用多部分表单数据上传文件
【发布时间】:2014-03-11 19:26:33
【问题描述】:

我想上传文件到 HTTP 服务器。我现在就是这样做的:

NSString *boundary = @"*****";

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

[request setURL:[NSURL URLWithString:@"http://someUploadScript.php"]];
[request setHTTPMethod:@"POST"];

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

[request setValue:@"Keep-Alive" forHTTPHeaderField: @"Connection"];

dispatch_async(queue, ^{
    NSMutableData *postbody = [NSMutableData data];

    [postbody appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [postbody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"userfile\"; filename=\"video.mp4\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
    [postbody appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];

    NSString* outputPath = @"somePathToFile";
    NSData *data = [NSData dataWithContentsOfFile:outputPath];

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

    [request setHTTPBody:postbody];
    previousBytesWritten = 0;
    connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
});

我想发送一些额外的数据,例如。用“userId”值提交“用户”。我想发送某种数组,例如:

video[user] = "userId"
video[file] = //file bytes

我知道我可以使用 HTTP multipartform-data 做到这一点,但我真的不知道怎么做,也不明白它是如何工作的。谁能解释一下我该怎么做以及它是如何工作的?

【问题讨论】:

    标签: ios http multipartform-data


    【解决方案1】:

    试试这样的:

    NSMutableData *postbody = [NSMutableData data];
    
    [postbody appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [postbody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"userfile\"; filename=\"video.mp4\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
    [postbody appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    
    NSString* outputPath = @"somePathToFile";
    NSData *data = [NSData dataWithContentsOfFile:outputPath];
    
    [postbody appendData:data];
    [postbody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    
    // Adding one more field:
    // append boundary
    [postbody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    // setting up form-data header, if it is text no 'filename' needed
    [postbody appendData:[@"Content-Disposition: form-data; name=\"userId\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    // appending userId value
    [postbody appendData:[_userId dataUsingEncoding:NSUTF8StringEncoding]];
    
    // Ending boundary
    [postbody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [postbody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    
    [request setHTTPBody:postbody];
    

    另外不要忘记在您的请求中添加“Content-Length”http 标头字段。

    【讨论】:

    • 如果我们只上传一个文件(没有额外的参数),Content-Length == data.length 吗?
    【解决方案2】:

    AFNetowrking 是当今的得心应手的解决方案。可以轻松实现。

    适用于 iOS 6 及更高版本

    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    NSDictionary *parameters = @{@"foo": @"bar"};
    NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"];
    [manager POST:@"http://example.com/resources.json" parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        [formData appendPartWithFileURL:filePath name:@"image" error:nil];
    } success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"Success: %@", responseObject);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Error: %@", error);
    }];
    

    如果项目是 iOS 7 及以上更好使用。

    NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
            [formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil];
        } error:nil];
    
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSProgress *progress = nil;
    
    NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
        if (error) {
            NSLog(@"Error: %@", error);
        } else {
            NSLog(@"%@ %@", response, responseObject);
        }
    }];
    
    [uploadTask resume];
    

    以上代码示例来自库文档。可以在https://github.com/AFNetworking/AFNetworking找到。

    【讨论】:

    • 请求失败:不可接受的内容类型:使用 AFNetworking 的 text/html
    【解决方案3】:

    您可以使用我的library 进行此类网络请求:

    WebRequest *request = [[WebRequest alloc] initWithPath:@"...documents/create.json"];
    request.delegate = delegate;
    request.notificationName = @"NotificationDocumentUploaded";
    
    NSMutableData *body = [NSMutableData data];
    NSString *boundary = @"TeslaSchoolProjectFormBoundary";
    
    [body appendPartName:@"document[name]" value:@"Test" boundary:boundary];
    [body appendPartName:@"document[description]" value:@"This is a description" boundary:boundary];
    [body appendPartName:@"document[category]" value:@"Drama" boundary:boundary];
    ...
    [body appendPartName:@"commit" value:@"Save" boundary:boundary];
    NSData *fileData = [[NSData alloc] initWithContentsOfURL:someFileURL];
    [body appendPartFile:fileName name:@"document[file]" data:fileData mimeType:mimeType boundary:boundary];
    [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    
    [request setHTTPBody:body];
    
    NSString *bodyLength = [NSString stringWithFormat:@"%lu",(unsigned long)[body length]];
    [request addValue:bodyLength forHTTPHeaderField:@"Content-Length"];
    
    // optional
    [request addValue:@"gzip,deflate,sdch" forHTTPHeaderField:@"Accept-Encoding"];
    [request addValue:@"max-age=0" forHTTPHeaderField:@"Cache-Control"];
    [request addValue:@"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" forHTTPHeaderField:@"Accept"];
    [request addValue:@"en-US,en;q=0.8,hr;q=0.6,it;q=0.4,sk;q=0.2,sl;q=0.2,sr;q=0.2" forHTTPHeaderField:@"Accept-Language"];
    
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"];
    
    [request setHTTPMethod:@"POST"];
    [WebRequestProcessor process:request];
    

    已设置代理以通知上传进度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-27
      • 1970-01-01
      • 1970-01-01
      • 2015-06-19
      • 1970-01-01
      相关资源
      最近更新 更多