iOS Swift中使用multipart/form-data上传图片数组仅最后一张成功的问题排查
问题分析与解决方案
你遇到的问题根源在于multipart/form-data的边界格式错误——你在循环里给每个图片都添加了请求结束的边界标记(--boundary--),导致服务器处理完第一个图片后就认为整个请求已经结束,后续的图片数据都被忽略了。
核心错误点
multipart请求的正确结构应该是:
- 每个表单字段(这里是每张图片)用
--{boundary}作为分隔符 - 所有字段结束后,才用
--{boundary}--标记整个请求的结束
而你的代码在循环内每次都用了--boundary--(结束标记),直接破坏了整个请求的结构,服务器自然只会识别到最后一段有效数据。
修正后的代码
下面是修复后的完整代码,同时还优化了几个潜在问题:
func uploadImage<T>(resource: Resource<T>, paramName: String, fileName: [String], images: [PLMUploadImage], completionHandler: @escaping (Result<T, PLMError>) -> Void) { let boundary = UUID().uuidString var urlRequest = URLRequest(url: resource.url) urlRequest.httpMethod = "POST" urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") if let accessToken = resource.accessToken { urlRequest.setValue( "\(PLMLOGINAPICONSTANT.BEARER)\(accessToken)", forHTTPHeaderField: PLMLOGINAPICONSTANT.AUTHORIZATION) } var data = Data() for (index, img) in images.enumerated() { // 1. 添加当前图片的分隔边界(注意是--boundary,不是--boundary--) data.append("--\(boundary)\r\n".data(using: .utf8)!) // 2. 设置表单字段的Content-Disposition let fileName = "\(index).jpg" data.append("Content-Disposition: form-data; name=\"\(paramName)\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!) // 3. 设置正确的Content-Type:因为你转成了JPEG,所以用image/jpeg而不是image/png data.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!) // 4. 添加图片数据,同时处理可选值避免崩溃 guard let imageData = img.image.jpeg(.medium) else { DispatchQueue.main.async { completionHandler(.failure(.invalidImageData)) // 建议新增这个错误类型 } return } data.append(imageData) // 5. 换行分隔下一个字段(如果有的话) data.append("\r\n".data(using: .utf8)!) } // 6. 添加整个请求的结束边界 data.append("--\(boundary)--\r\n".data(using: .utf8)!) let session = URLSession.shared session.uploadTask(with: urlRequest, from: data) { responseData, response, error in DispatchQueue.main.async { // 统一回到主线程处理结果 if let error = error { completionHandler(.failure(.networkError(error))) // 建议新增网络错误类型 return } guard let responseData = responseData else { completionHandler(.failure(.emptyResponse)) // 建议新增空响应错误类型 return } do { let result = try JSONDecoder().decode(T.self, from: responseData) completionHandler(.success(result)) // 可选:打印响应日志 if let json = try? JSONSerialization.jsonObject(with: responseData, options: .allowFragments) as? [String: Any] { PLMLogger.prettyPrint(data: json) } } catch { completionHandler(.failure(.decodingError)) } } }.resume() }
额外优化说明
- Content-Type匹配:你将图片转成了JPEG格式,所以Content-Type应该设为
image/jpeg,之前的image/png会导致服务器识别格式错误。 - 可选值安全处理:对
img.image.jpeg(.medium)的结果做了guard判断,避免nil导致的崩溃,同时新增了对应的错误类型(你需要在PLMError里补充这些case)。 - 主线程统一处理:将所有completion的调用都放到主线程,避免UI更新时的线程问题。
- 错误处理完善:补充了网络错误、空响应等场景的错误处理,让逻辑更健壮。
内容的提问来源于stack exchange,提问作者govind kumar
相关产品推荐
相关产品推荐

