多部分表单上传和NSURLSession.uploadTaskWithRequest

时间:2015-10-14 06:27:40

标签: ios multipartform-data nsurlsession

来自网络编程世界,我对使用多部分表单请求上传文件感到非常自在。但是,在iOS中,我们有一个名为NSURLSession的东西,其方法为uploadTaskWithRequest,这似乎是调用图像上传等方法的方法。

您能否解释两种方法之间的区别,多部分表单上传uploadTaskWithRequest?如果我已经有一个处理多部分表单上传的后端,我可能需要进行哪些调整以便它也支持uploadTaskWithRequest

3 个答案:

答案 0 :(得分:11)

uploadTaskWithRequest只是将NSData,文件或流作为请求正文发送。除此之外,它没有做任何事情。它只是有一个好处,它可以用于后台会话。

因此,如果您有期望multipart/form-data请求的Web服务,您必须自己构建该请求(除非您使用AFNetworking或Alamofire之类的东西为您执行此操作)。在您构建了该请求后,您可以使用dataTaskWithRequest(设置HTTPBody的{​​{1}})或NSMutableURLRequest(在这种情况下,您不能使用&#39} ; t设置uploadTaskWithRequest,而是将其作为HTTPBody}的参数提供。

顺便说一句,像Charles这样的工具在这些情况下非常有用,让您可以观察幕后发生的事情。

答案 1 :(得分:2)

使用multipart/form-data

上传文件

使用multipart/form-data内容类型的第一种方法最初在RFC 1867中定义,然后转移到万维网联盟,其中包含它在HTML 4.0的规范中,其中表单用HTML表示,表单值通过HTTP和电子邮件发送。当表单已由用户填写时,表单被发送到服务器。浏览器和Web服务器广泛支持和使用此技术。

但是,multipart/form-data也可用于定义表单数据,这些表单数据以HTML之外的其他表示形式呈现。也就是说,您不一定需要Web浏览器或Web服务器。可由各种应用程序使用并通过各种协议传输的当前规范是RFC 7578(来自IETF)。

但必须提到的是,multipart/form-data内容类型并非始终/并非没有问题。它本身相当复杂。此外,它使用/指代许多其他RFC,并且 - 由于清理 - 它和它所依赖的那些已经被更改,过时和更新频繁。由于其复杂性,序列化程序和解析器也变得非常复杂,并且存在很多错误和其他问题的空间。

NSURLSession uploadTaskWithRequest

NSURLSession如何撰写请求并未准确记录。但它肯定不会使用multipart/form-data内容类型。

对于上传任务,NSURLSession使用带有NSURLRequest参数的POST请求,您可以自行设置。也就是说,您可以选择设置内容类型(例如text/plain; charset=utf-8)和其他标题。NSURLSession也可以从给定内容(文件,流或NSData)中派生出适当的内容类型。我们可以说,它变成了一个简单的POST请求。由于复杂性较低,请求不那么麻烦。

因此,为了让您的服务器支持应该上传文件的uploadTaskWithRequest,它应该只支持一些"简单"的POST请求。内容类型。也就是说,而不是"文件上传"如果内容类型为multipart/form-data,其中包含处置标头中的文件名,则服务器需要返回已写入资源(文件)的位置的URL。

答案 2 :(得分:0)

幸运的是,在后台进行多部分/表单数据POST请求非常容易,例如,如果您要上传图像以及其他信息。

首先,以与同步请求相同的方式创建NSMutableURLRequest(例如,参见POST multipart/form-data with Objective-C)。

然后,将请求的主体写入文件,并将其提供给使用backgroundSessionConfiguration创建的NSURLSession的uploadTaskWithRequest方法:

NSString *filePath = [[NSSearchPathForDirectoriesInDomains(
                           NSCachesDirectory, NSUserDomainMask, YES) lastObject] 
                           stringByAppendingPathComponent:imageUUID];

[request.HTTPBody writeToFile:filePath atomically:YES];

NSURLSessionUploadTask *task = [urlSession uploadTaskWithRequest:request 
                                           fromFile:[NSURL fileURLWithPath:filePath]];
[task resume];

如果您有多个任务,并且希望能够在委托回调中区分它们,则可以使用NSURLProtocol类设置参数(创建请求后):

[NSURLProtocol setProperty:imageUUID
                    forKey:@"yourKeyForTheImageUUID" 
                 inRequest:request];

并像这样在回调中将其恢复:

- (void)URLSession:(NSURLSession *)session
        task:(nonnull NSURLSessionTask *)task
        didCompleteWithError:(nullable NSError *)error
{
    NSString *imageUUID = [NSURLProtocol propertyForKey:@"yourKeyForTheImageUUID"
                                              inRequest:task.originalRequest];