NSURLSession 下载:URL 使用详解及避坑指南
2025-02-01 08:26:53
NSURLSession 下载任务 URL 使用详解
在使用 NSURLSession
进行网络数据下载时,downloadTaskWithURL
方法中的 URL 参数经常使人困惑。问题核心在于,为何需要传入完整查询 URL (queryURL
),而不是基础 URL (baseURL
)? 这涉及 URL 的构造和 NSURLSession
的工作机制。下面进行分析说明。
URL 的正确构造
理解问题的关键在于 URL 的构造过程。HTTP 请求通常需要完整的 URL 来指定资源位置。一个 URL 包含几个部分,包括:协议(例如 "http" 或者 "https"),主机名或IP地址,以及资源路径和查询参数。
在这个例子中, baseURL
指定了基础 URL 地址。为了发起特定资源的请求,就需要加上具体的查询参数(如 “&q=onion+soup”)。通过 NSURL(string: relativeToURL:)
的方法可以将查询参数附加到 baseURL
,构建出完整的 queryURL
。
解决方案一: 使用完整URL发起下载
NSURLSession
需要一个指向完整资源的 URL 进行下载。因此, downloadTaskWithURL
必须传入 queryURL
。这个 queryURL
是包含了查询参数的完整URL。这是其根本作用。
代码示例如下:
func searchRecipeData() {
let baseURL = NSURL(string: "http://api.recipes.com/v1/api/recipes?_app_id=YOUR_API_ID&_app_key=YOUR_API_KEY")!
let queryURL = NSURL(string: "&q=onion+soup", relativeToURL: baseURL)!
let sharedSession = NSURLSession.sharedSession()
let downloadTask = sharedSession.downloadTaskWithURL(queryURL) { (location: NSURL?, response: NSURLResponse?, error: NSError?) -> Void in
if let error = error {
print("Error downloading data: \(error)")
return
}
guard let location = location else {
print("No temporary location found after download")
return
}
//移动临时下载文件到指定目录 (通常为本地文件系统)
let fileManager = NSFileManager.defaultManager()
let documentPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first!
let finalFilePath = NSURL(fileURLWithPath: documentPath + "/recipe_download.json")
do{
try fileManager.moveItemAtURL(location, toURL: finalFilePath)
print("File successfully downloaded and stored: \(finalFilePath)")
let data = try NSData(contentsOfURL: finalFilePath)
print(data)
} catch {
print("Unable to move/get data at file \(location)")
}
}
downloadTask.resume()
}
操作步骤:
- 将代码粘贴到 Swift 项目的 ViewController 中
- 用你自己的
API_ID
和API_KEY
替换占位符,或者选择其他不需要apiID
与apiKey
的baseURL
- 编译并运行,程序会将下载内容存储在Documents目录。
- 如果在终端下运行,会在控制台打印
data
对象的内容
此代码首先通过给定的baseURL生成queryURL, 然后启动网络下载任务,在回调函数中处理下载结果。location
参数是临时文件的本地路径,可以从中读取下载内容。 我们通常使用 fileManager.moveItemAtURL()
移动文件,方便进行下一步处理。注意需要错误处理。
** 安全建议**
- API Keys等敏感信息不要直接写在代码中,而是要采用更为安全的方式例如存储在密钥链或环境变量中。
- 务必在生产环境中对数据进行适当的验证。
- 需要添加异常处理代码以避免程序在不处理错误的情况下发生崩溃。
- 根据不同的业务需求选择不同的
NSURLSessionConfiguration
。例如,可以使用NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier
方法来创建一个支持在后台运行的网络会话,即便应用切换到后台或者被关闭,下载任务依然可以继续。
错误用法剖析
之前错误的代码尝试用 baseURL
来读取内容是不正确的,因为它并没有实际下载数据, 而是在内存里使用之前定义的 baseURL
这个字串创建一个 NSURL
对象去加载内存中缓存或者无效数据, 这个地址是没有下载的。 这样的操作在实际情况中将可能得到错误或者无法预期的数据。NSURLSessionDownloadTask
在后台下载结束后将返回一个临时的文件路径, 这个路径才是需要处理的。
代码示例 (错误):
func searchRecipeData() {
let baseURL = NSURL(string: "http://api.recipes.com/v1/api/recipes?_app_id=\(apiID)&_app_key=\(apiKey)")!
let queryURL = NSURL(string: "&q=onion+soup", relativeToURL: baseURL)!
let sharedSession = NSURLSession.sharedSession()
let downloadData: NSURLSessionDownloadTask = sharedSession.downloadTaskWithURL(queryURL, completionHandler: { (location: NSURL?, response: NSURLResponse?, error: NSError?) -> Void in
if (error == nil) {
let data = NSData(contentsOfURL: baseURL) // 错误: 从 baseURL 中尝试加载数据。
print(data)
} else{
print(error)
}
})
downloadData.resume()
}
代码解释:
let data = NSData(contentsOfURL: baseURL)
这一行,应该改为读取下载到 location
指向的文件。因为,当一个下载任务完成后,其结果不会被存入 baseURL对应的url中。它仅仅返回一个本地下载文件路径(location),只有处理location指向的文件内容才是正确的做法。 使用原始 baseURL
获取数据将获取不到实际的下载数据内容。
总而言之,NSURLSessionDownloadTask
的核心在于下载指定 URL 指向的资源到本地文件。 使用正确的URL 以及在下载完成回调中使用正确的处理逻辑才能成功获取到所需要的数据内容。 理解 URL 的构成和 NSURLSession
的工作原理是使用 downloadTask
的关键。