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

如何使用URLSession在后台上传Zip文件?服务器未接收到文件的问题排查

解决后台上传Zip文件时服务器无法获取文件的问题

看起来你遇到的核心问题是:虽然请求的Content-Length显示正确,但服务器端没法拿到上传的Zip文件。这主要是因为你当前的请求格式和服务器的期望不匹配,下面给你拆解原因和解决方案:

为什么当前实现不行?

你设置了Content-Type: application/zip,这是把Zip文件的二进制数据直接作为请求体发送。但大多数服务器框架(比如Express、Django、Laravel等)默认是处理multipart/form-data格式的文件上传——也就是把文件作为表单的一个字段来传递,而不是直接传原始二进制。这就导致服务器端的request.files为空,body也因为框架没解析原始二进制而显示为空。

解决方案一:改成multipart/form-data格式上传(推荐)

这是最通用的方式,适配绝大多数服务器的默认文件上传处理逻辑。你需要先把Zip文件包装成multipart格式的数据,再发送请求。

步骤1:添加构造multipart数据的辅助函数

func createMultipartFormData(filePath: URL, fieldName: String) throws -> Data {
    let boundary = "Boundary-\(UUID().uuidString)"
    var data = Data()
    
    // 添加上传字段的头部信息
    data.append("--\(boundary)\r\n".data(using: .utf8)!)
    data.append("Content-Disposition: form-data; name=\"\(fieldName)\"; filename=\"\(filePath.lastPathComponent)\"\r\n".data(using: .utf8)!)
    data.append("Content-Type: application/zip\r\n\r\n".data(using: .utf8)!)
    
    // 添加Zip文件的二进制数据
    let fileData = try Data(contentsOf: filePath)
    data.append(fileData)
    
    // 添加multipart的结束边界
    data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
    
    return data
}

步骤2:修改上传函数

func uploadZip(filePath: URL, authorization: String, callback: @escaping ((Bool) -> Void)) {
    BGUploadManager.callback = callback
    let url = URL(string: "\(APINetwork.BASE_URL)")!
    var request = URLRequest(url: url)
    request.headers = ["Authorization": "Bearer " + authorization]
    request.method = HTTPMethod(rawValue: "POST")
    
    do {
        // 这里的"zipFile"要和服务器端期望的字段名一致,比如服务器用multer的话,要对应single("zipFile")
        let multipartData = try createMultipartFormData(filePath: filePath, fieldName: "zipFile")
        let boundary = "Boundary-\(UUID().uuidString)"
        // 设置正确的Content-Type
        request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
        
        let config = URLSessionConfiguration.background(withIdentifier: "identifier")
        config.isDiscretionary = false
        let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
        // 用uploadTask发送multipart数据
        let task = session.uploadTask(with: request, from: multipartData)
        task.resume()
    } catch {
        print("生成multipart数据失败: \(error)")
        callback(false)
    }
}

服务器端适配(以Express为例)

如果你用Express,需要用multer中间件来处理multipart上传:

const multer = require('multer');
const upload = multer({ dest: 'uploads/' }); // 指定文件保存目录

app.post('/your-upload-endpoint', upload.single('zipFile'), (req, res) => {
    // req.file就是上传的Zip文件
    console.log('上传的文件:', req.file);
    res.status(200).send('文件上传成功');
});

解决方案二:服务器端直接读取原始请求体

如果你的服务器确实需要接收application/zip格式的原始二进制,那服务器端要跳过表单解析,直接读取请求的原始body。

以Express为例,需要用express.raw中间件:

const fs = require('fs');

app.post('/your-upload-endpoint', express.raw({ type: 'application/zip', limit: '100mb' }), (req, res) => {
    // req.body就是Zip文件的二进制数据
    fs.writeFile('uploaded.zip', req.body, (err) => {
        if (err) {
            return res.status(500).send('保存文件失败');
        }
        res.send('文件上传成功');
    });
});

这种情况下,你的客户端代码不需要修改,但兼容性较差,只适合特定场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 23:37:28