如何使用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
相关产品推荐
相关产品推荐

