来自网络编程世界,我对使用多部分表单请求上传文件感到非常自在。但是,在iOS中,我们有一个名为NSURLSession
的东西,其方法为uploadTaskWithRequest
,这似乎是调用图像上传等方法的方法。
您能否解释两种方法之间的区别,多部分表单上传与uploadTaskWithRequest
?如果我已经有一个处理多部分表单上传的后端,我可能需要进行哪些调整以便它也支持uploadTaskWithRequest
?
答案 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];