我们可以为一个HTTP的POST请求提供请求体(request body)内容,这三种方式分别是:NSData对象、文件或者数据流。总的来说,我们在程序中应该:

  • 如果我们程序内存中已经存在了data数据,那么我们没有理由再去处理它们,而是直接使用NSData对象
  • 如果上传的内容是存在于磁盘的文件时,或者是想要在后台进行传输,抑或是想将相关数据写入磁盘以便于释放掉与该数据相关联的内存时,就使用文件的方式
  • 如果是从网络接收数据时,使用数据流的方式

无论是选择哪种方式,如果我们在app中提供了自定义的session代理的话,该代理对象需要实现URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:方法以获得上传进度信息。

另外,如果是使用数据流方式提供的请求体,我们需要提供一个自定义的session代理对象,而且该代理对象还必须要实现URLSession:task:needNewBodyStream:方法,更多详细信息会在后面讲解。

使用NSData对象的方式上传请求体

使用NSData对象作为请求体上传,我们可以使用uploadTaskWithRequest:fromData:方法或者uploadTaskWithRequest:fromData:completionHandler:方法来创建一个上传任务,使用形参fromData来传递请求体数据。

session会基于数据对象的大小来计算首部Content-Length的值。

我们应该要提供服务器需要的其他的首部字段作为URL请求对象的一部分,例如content type。

/// Objective-C
NSURL *textFileURL = [NSURL fileURLWithPath:@"/path/to/file.txt"];
NSData *data = [NSData dataWithContentsOfURL:textFileURL];

NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:url];
mutableRequest.HTTPMethod = @"POST";
[mutableRequest setValue:[NSString stringWithFormat:@"%lld", data.length] forHTTPHeaderField:@"Content-Length"];
[mutableRequest setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];

NSURLSessionUploadTask *uploadTask = [defaultSession uploadTaskWithRequest:mutableRequest fromData:data];
[uploadTask resume];
/// Swift
let textFileURL = URL(fileURLWithPath: "/path/to/file.txt")
if let data = try? Data(contentsOf: textFileURL) {
    if let url = URL(string: "https://www.example.com/") {
        var mutableRequest = MutableURLRequest(url: url)
        mutableRequest.httpMethod = "POST"
        mutableRequest.setValue("\(data.count)", forHTTPHeaderField: "Content-Length")
        mutableRequest.setValue("text/plain", forHTTPHeaderField: "Content-Type")

        let uploadTask = defaultSession.uploadTask(with: mutableRequest, from: data)
        uploadTask.resume()
    }
}

使用文件的方式上传请求体

把文件作为内容上传,我们可以调用 uploadTaskWithRequest:fromFile:方法或者 uploadTaskWithRequest:fromFile:completionHandler:方法去创建一个上传任务,并提供上传任务读取请求体内容的一个文件URL。

session同样是根据数据对象来计算首部Content-Length值,如果我们没有提供首部的Content-Type值,则session会提供一个默认值,同样我们也需要提供一些服务器需要的请求头字段。

/// Objective -C
NSURL *textFileURL = [NSURL fileURLWithPath:@"/path/to/file.txt"];
NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:url];
mutableRequest.HTTPMethod = @"POST";
NSURLSessionUploadTask *uploadTask = [defaultSession uploadTaskWithRequest:mutableRequest fromFile:textFileURL];
[uploadTask resume];
/// Swift
let textFileURL = URL(fileURLWithPath: "/path/to/file.txt")
if let url = URL(string: "https://www.example.com/") {
    var mutableRequest = MutableURLRequest(url: url)
    mutableRequest.httpMethod = "POST"
    let uploadTask = defaultSession.uploadTask(with: mutableRequest, from: textFileURL)
    uploadTask.resume()
}

使用数据流的方式上传请求体

数据流作为请求体来上传的话,我们可以调用uploadTaskWithStreamedRequest:方法来创建一个上传任务,我们需要提供该任务请求体相关的数据流,和前面两个类似要上传服务器所需的请求头字段,比如Content-Type,Content-Length等等。

另外,由于session不一定能够将数据流重置来重新读取数据,所以我们需要在session重新请求的时候提供新的数据流(比如在身份验证失败后,需要重新发送请求)。基于此,我们需要在程序中提供URLSession:task:needNewBodyStream:代理方法,当该方法被调用时,需要我们去执行获取或者创建新数据流的所有操作,并使用新的数据流调用提供的completion handler。

completionHandler(newbodyStream);

提示

如果我们使用数据流的方式来提供请求体,由于我们必须要提供URLSession:task:needNewBodyStream: 代理方法,所以该技术不能使用系统提供的代理。

/// Objective -C
NSURL *textFileURL = [NSURL fileURLWithPath:@"/path/to/file.txt"];

NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:url];
mutableRequest.HTTPMethod = @"POST";
mutableRequest.HTTPBodyStream = [NSInputStream inputStreamWithFileAtPath:textFileURL.path];
[mutableRequest setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
[mutableRequest setValue:[NSString stringWithFormat:@"%lld", data.length] forHTTPHeaderField:@"Content-Length"];

NSURLSessionUploadTask *uploadTask = [defaultSession uploadTaskWithStreamedRequest:mutableRequest];
[uploadTask resume];
/// Swift
let textFileURL = URL(fileURLWithPath: "/path/to/file.txt")
if let url = URL(string: "https://www.example.com/") {
    var mutableRequest = MutableURLRequest(url: url)
    mutableRequest.httpMethod = "POST"
    mutableRequest.httpBodyStream = InputStream(fileAtPath: textFileURL.path!)
    mutableRequest.setValue("\(data.count)", forHTTPHeaderField: "Content-Length")
    mutableRequest.setValue("text/plain", forHTTPHeaderField: "Content-Type")

    let uploadTask = defaultSession.uploadTask(with: mutableRequest, from: textFileURL)
    uploadTask.resume()
}

使用Download Task上传文件

使用download task来上传请求体内容,当创建一个download请求时,只能使用NSData对象或者数据流来作为该NSURLRequest对象的一部分。如果是使用的是数据流的方式,上面也讲过需要实现URLSession:task:needNewBodyStream:代理方法,在身份验证失败时提供一个新的数据流。这些内容具体在上一节“使用数据流的方式上传请求体”中有详细讲解。

除了数据返回app的方式不同之外,其他方面下载任务和数据任务是相似的。

results matching ""

    No results matching ""