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

Swift中URLSession非Multipart方式文件上传进度获取方法

如何在Swift的URLSession中获取非Multipart方式的文件上传进度?

嗨,我来帮你搞定这个上传进度的问题~你当前用的URLSession.shared.dataTask(with:)确实没法拿到上传进度,因为这个方法是把整个请求体(也就是你的视频数据)先全部加载到内存,再一次性发送,而且它本身没有提供进度回调的接口。要获取实时上传进度,得换个方式——用**URLSession的上传任务(uploadTask)**配合代理方法来实现,具体步骤如下:

核心思路

通过实现URLSessionTaskDelegate协议中的urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)方法,这个方法会在上传数据的过程中被反复调用,里面的参数直接提供了已发送字节数和总字节数,我们可以用它来计算上传进度。

修改后的完整实现

首先让你的类遵守URLSessionTaskDelegate协议(比如放在ViewController或者专门的上传管理类里),然后替换原来的上传代码:

class VideoUploadManager: NSObject, URLSessionTaskDelegate {
    private var token: String = "你的实际Token" // 替换成你的授权Token
    
    func startVideoUpload() {
        let urlStr = UserDefaults.standard.value(forKey: "Resumable") as? String ?? ""
        guard let url = URL(string: urlStr) else {
            print("无效的上传URL")
            return
        }
        
        do {
            var request = try URLRequest(url: url, method: .put)
            // 设置请求头
            request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
            request.setValue("video/*", forHTTPHeaderField: "Content-Type")
            request.timeoutInterval = 60.0
            
            // 加载视频文件(推荐用URL方式,避免大文件占用过多内存)
            guard let videoFileUrl = Bundle.main.url(forResource: "video", withExtension: "mov") else {
                print("找不到指定的视频文件")
                return
            }
            // 获取文件大小,设置Content-Length(可选,部分服务器会自动处理)
            let fileSize = try FileManager.default.attributesOfItem(atPath: videoFileUrl.path)[.size] as? Int64 ?? 0
            request.setValue("\(fileSize)", forHTTPHeaderField: "Content-Length")
            
            // 创建带代理的URLSession
            let sessionConfig = URLSessionConfiguration.default
            // 指定代理队列为主线程,方便直接更新UI
            let session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: OperationQueue.main)
            
            // 使用uploadTask从文件上传(大文件推荐这种方式,内存友好)
            let uploadTask = session.uploadTask(with: request, fromFile: videoFileUrl) { [weak self] data, response, error in
                guard let self = self else { return }
                
                if let error = error {
                    print("上传失败:\(error.localizedDescription)")
                    return
                }
                
                guard let httpResponse = response as? HTTPURLResponse else {
                    print("无效的服务器响应")
                    return
                }
                
                print("服务器响应头:\(httpResponse.allHeaderFields)")
                if httpResponse.statusCode == 200 {
                    if let locationUrl = httpResponse.allHeaderFields["Location"] as? String {
                        print("上传成功!返回的资源URL:\(locationUrl)")
                    }
                } else {
                    print("上传失败,状态码:\(httpResponse.statusCode)")
                }
            }
            
            uploadTask.resume()
        } catch {
            print("创建请求或读取文件失败:\(error.localizedDescription)")
        }
    }
    
    // MARK: - URLSessionTaskDelegate
    func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
        // 计算上传进度
        let uploadProgress = Double(totalBytesSent) / Double(totalBytesExpectedToSend)
        // 打印进度,或者更新UI(比如进度条)
        print("当前上传进度:\(String(format: "%.2f%%", uploadProgress * 100))")
        
        // 如果要更新UI,直接在这里操作即可(因为我们指定了代理队列为主线程)
        // self.progressView.progress = uploadProgress
    }
}

几个关键注意点

  • 不要硬编码Content-Length:用文件的实际大小来设置,避免出现数据长度不匹配导致的上传失败。
  • 大文件推荐用fromFile参数:直接传入文件URL,系统会分块读取文件并上传,不会把整个文件加载到内存,避免内存过高的问题。
  • 代理队列的选择:如果需要更新UI,一定要把代理队列设置为主线程(OperationQueue.main),否则直接操作UI会崩溃;如果不需要更新UI,可以用默认的后台队列。

内容的提问来源于stack exchange,提问作者TechChain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:44:02