如何不使用URLSessionDownloadTask自动分块功能下载大文件?
解决URLSessionDownloadTask分块下载不兼容非常规服务器的问题
嘿,碰到这种只允许单次连接下载的服务器确实挺闹心的——默认的URLSessionDownloadTask为了优化大文件下载效率,会自动拆分请求成多块,但这种非常规服务器根本不吃这一套,后续分块请求自然全失败。给你两个靠谱的解决方案:
方案一:改用URLSessionDataTask手动处理文件保存
这是最稳妥的方法,DataTask会以单次请求获取完整的响应数据,不会自动分块。你只需要在回调里累积数据,最后写入磁盘就行:
import Foundation let targetURL = URL(string: "你的下载文件地址")! var accumulatedData = Data() // 创建请求,可选添加头信息强化单次连接要求 var downloadRequest = URLRequest(url: targetURL) downloadRequest.setValue("bytes=0-", forHTTPHeaderField: "Range") // 明确要求下载整个文件 downloadRequest.setValue("close", forHTTPHeaderField: "Connection") // 告诉服务器请求完成后关闭连接 let session = URLSession.shared let dataTask = session.dataTask(with: downloadRequest) { data, response, error in // 处理错误 if let error = error { print("下载失败:\(error.localizedDescription)") return } // 验证响应状态 guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { print("服务器响应异常,状态码不符合预期") return } // 累积数据 if let chunkData = data { accumulatedData.append(chunkData) } // 下载完成后写入文件 let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let savePath = documentsDir.appendingPathComponent("下载的文件名.ext") do { try accumulatedData.write(to: savePath) print("文件已成功保存到:\(savePath.path)") } catch { print("保存文件失败:\(error.localizedDescription)") } } dataTask.resume()
方案二:配置DownloadTask禁用分块请求
如果你更想继续用DownloadTask,可以通过修改请求头来阻止URLSession发起分块请求。不过这个方法的可靠性取决于服务器是否遵守HTTP头约定:
import Foundation let targetURL = URL(string: "你的下载文件地址")! var downloadRequest = URLRequest(url: targetURL) // 关键:设置Range头为从0开始的完整范围,告诉服务器返回整个文件 downloadRequest.setValue("bytes=0-", forHTTPHeaderField: "Range") let session = URLSession.shared let downloadTask = session.downloadTask(with: downloadRequest) { location, response, error in if let error = error { print("下载失败:\(error.localizedDescription)") return } guard let tempLocation = location, let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { print("下载响应异常") return } // 将临时文件移动到指定位置 let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let savePath = documentsDir.appendingPathComponent("下载的文件名.ext") do { // 如果目标文件已存在,先删除 if FileManager.default.fileExists(atPath: savePath.path) { try FileManager.default.removeItem(at: savePath) } try FileManager.default.moveItem(at: tempLocation, to: savePath) print("文件已成功保存到:\(savePath.path)") } catch { print("保存文件失败:\(error.localizedDescription)") } } downloadTask.resume()
额外提醒
- 如果服务器完全忽略
Range头,方案二可能失效,这时候方案一就是最优选择。 - 对于超大文件,
DataTask可能会占用较多内存,你可以改成在回调中边接收数据边写入文件,避免全部数据累积到内存里。
内容的提问来源于stack exchange,提问作者jamone
相关产品推荐
相关产品推荐

