You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何不使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:24:42