Alamofire 5.2多图+参数Multipart表单上传问题求助
Alamofire 5.2 多图上传及进度监听问题解答
问题背景
我正尝试使用Alamofire 5.2向服务器上传多张图片及参数,服务器期望的格式为:
[{"key":"media1","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]}, {"key":"media2","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]}, {"key":"media3","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]}, {"key":"model","value":"{\"emp_Id\": \"6\",\"device_Identifier\":\"Device 01\",\"timestamp\":\"123\",\"description\":\"description\",\"handoverStatus\":\"false\"}","type":"text"}]我将选中的图片添加到数组的代码如下:
var dictImage : [UIImage]? if pickedImage.pngData() != nil { dictImage!.append(pickedImage) }我当前调用Alamofire上传的函数如下:
func xyz() { let param : [String: Any] = [ "emp_Id" : "", "device_Identifier" : "", "timestamp" : "", "description" : "", "handoverStatus" : "" ] let urlString = URL(string: APIDetails.BaseUrl + "api/123")! let url = (try? URLRequest(url: urlString, method: .post, headers: APIDetails.header))! AF.upload(multipartFormData: { (multipartData) in let img1 = arrImage![0] let img2 = arrImage![1] let img3 = arrImage![2] multipartData.append(img1.pngData(), withName:"media1", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file") multipartData.append(img2.pngData(), withName:"media2", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file") multipartData.append(img3.pngData(), withName:"media3", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file") for (key, value) in param { multipartData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key) } }, with: url) }我有以下问题:
- 如何以字典形式上传每张图片及其详情?
- 如何添加上传进度监听及结果反馈(判断上传成功或失败)?
我尝试添加encodingCompletion回调:}, with: url,encodingCompletion:{ EncodingResult in switch EncodingResult{ case .success(let upload, _, _): upload.responseJSON { response in debugPrint("SUCCESS RESPONSE: \(response)") } case .failure(let encodingError): print("ERROR RESPONSE: \(encodingError)") } })但出现报错:
Extra argument 'encodingCompletion' in call。这是我第一次尝试上传Multipart表单数据,部分代码参考自其他回答。
问题1:按服务器期望的字典格式上传图片及参数
首先得明确服务器的要求:所有数据(图片+文本参数)都要以数组字典的形式提交,其中图片对应mediaN的字典项,文本参数打包成model的JSON字符串。我们需要调整MultipartFormData的构造逻辑:
步骤1:把文本参数转为JSON字符串
服务器的model字段value是一个JSON字符串,所以先把你的参数字典转成符合要求的字符串:
let paramDict: [String: Any] = [ "emp_Id": "6", "device_Identifier": "Device 01", "timestamp": "123", "description": "description", "handoverStatus": "false" ] // 转成JSON字符串,失败则直接返回 guard let modelData = try? JSONSerialization.data(withJSONObject: paramDict), let modelString = String(data: modelData, encoding: .utf8) else { print("参数转JSON失败") return }
步骤2:按服务器格式构造图片字段
服务器要求每个图片对应description、type、value(数组)三个字段,所以在Multipart表单里要按mediaN[字段名]的格式append数据:
// 在multipartFormData闭包里处理图片 for (index, image) in arrImage.enumerated() { guard let imageData = image.pngData() else { continue } let mediaKey = "media\(index + 1)" // 添加上description(空字符串) multipartData.append("".data(using: .utf8)!, withName: "\(mediaKey)[description]") // 添加上type(固定为file) multipartData.append("file".data(using: .utf8)!, withName: "\(mediaKey)[type]") // 添加上图片文件,注意value是数组,所以字段名要加[] multipartData.append(imageData, withName: "\(mediaKey)[value][]", fileName: "\(Date().timeIntervalSince1970)_media\(index+1).jpg", mimeType: "image/jpeg") // 这里用image/jpeg更规范,不要用file }
步骤3:添加上model参数
同样按照服务器的格式,给model添加value和type字段:
multipartData.append(modelString.data(using: .utf8)!, withName: "model[value]") multipartData.append("text".data(using: .utf8)!, withName: "model[type]")
问题2:添加上传进度监听和结果反馈
你之前用encodingCompletion报错是因为Alamofire 5.x已经废弃了这个回调,现在改用链式调用uploadProgress和response系列方法即可。
完整的上传函数示例
func uploadMedia() { // 先检查图片数组是否为空,避免崩溃 guard let selectedImages = arrImage, !selectedImages.isEmpty else { print("请先选择图片") return } // 1. 构造并转换文本参数 let paramDict: [String: Any] = [ "emp_Id": "6", "device_Identifier": "Device 01", "timestamp": "123", "description": "description", "handoverStatus": "false" ] guard let modelData = try? JSONSerialization.data(withJSONObject: paramDict), let modelString = String(data: modelData, encoding: .utf8) else { print("参数转JSON失败") return } // 2. 构造请求URL和Request guard let url = URL(string: APIDetails.BaseUrl + "api/123"), var urlRequest = try? URLRequest(url: url, method: .post, headers: APIDetails.header) else { print("URL或请求构造失败") return } // 3. 发起上传请求,链式处理进度和结果 AF.upload(multipartFormData: { multipartData in // 处理图片 for (index, image) in selectedImages.enumerated() { guard let imageData = image.pngData() else { continue } let mediaKey = "media\(index + 1)" multipartData.append("".data(using: .utf8)!, withName: "\(mediaKey)[description]") multipartData.append("file".data(using: .utf8)!, withName: "\(mediaKey)[type]") multipartData.append(imageData, withName: "\(mediaKey)[value][]", fileName: "\(Date().timeIntervalSince1970)_media\(index+1).jpg", mimeType: "image/jpeg") } // 处理model参数 multipartData.append(modelString.data(using: .utf8)!, withName: "model[value]") multipartData.append("text".data(using: .utf8)!, withName: "model[type]") }, with: urlRequest) // 监听上传进度 .uploadProgress { progress in let progressPercent = progress.fractionCompleted * 100 print("当前上传进度:\(String(format: "%.2f", progressPercent))%") // 更新UI要回到主线程 DispatchQueue.main.async { // 比如更新进度条:self.progressView.progress = progress.fractionCompleted } } // 处理上传结果 .responseJSON { response in switch response.result { case .success(let responseData): debugPrint("上传成功!返回数据:\(responseData)") // 这里写上传成功后的业务逻辑,比如弹窗提示、跳转页面等 case .failure(let error): print("上传失败!错误信息:\(error.localizedDescription)") // 处理失败逻辑,比如提示用户重新上传 } } }
额外的小优化:修复图片数组初始化问题
你之前的dictImage是可选类型,直接强制解包容易崩溃,建议改成非可选的空数组:
// 原来的代码 // var dictImage : [UIImage]? // if pickedImage.pngData() != nil { // dictImage!.append(pickedImage) // } // 修改后的代码 var dictImage: [UIImage] = [] // 初始化空数组 if pickedImage.pngData() != nil { dictImage.append(pickedImage) }
内容的提问来源于stack exchange,提问作者Zyfe3r
相关产品推荐
相关产品推荐

