Swift 4 + Alamofire 4.7多部分上传失败,请求协助排查
你遇到的NSCocoaErrorDomain Code=3840序列化错误,核心原因是Alamofire尝试把服务器响应解析成JSON,但服务器返回的内容根本不是合法的JSON格式——可能是空字符串、纯文本提示、HTML错误页,甚至是格式损坏的JSON。虽然数据已经成功写入服务器,但响应解析失败导致你拿不到预期结果。
第一步:先搞清楚服务器到底返回了什么
先把代码里的responseJSON换成responseString,打印出服务器的原始响应,这样就能直接定位问题出在响应内容上:
upload.responseString { response in print("服务器原始响应:\(response.result.value ?? "无响应内容")") // 尝试手动解析JSON,验证是否合法 if let responseStr = response.result.value, let data = responseStr.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: data, options: []) { print("手动解析JSON成功:\(json)") onCompletion?(json as? JSON) } else { print("响应不是合法JSON格式") // 这里可以根据实际返回内容调整处理逻辑,比如直接返回字符串给上层 } if let err = response.error { onError?(err) return } }
运行后看控制台输出,就能明确服务器返回的具体内容——比如可能是"上传成功"这类纯文本,或者是格式错误的JSON,甚至是空响应。
第二步:修正上传代码里的两个关键问题
你的上传代码有两个容易引发异常的细节:
1. 不要手动设置Content-type: multipart/form-data请求头
Alamofire处理multipartFormData上传时,会自动生成包含boundary的正确Content-Type头(比如multipart/form-data; boundary=xxx)。你手动设置的头会覆盖这个自动生成的内容,丢失关键的boundary信息,可能导致服务器解析请求异常,进而返回非预期响应。
删掉手动设置的Content-Type,只保留Authorization头:
let headers = ["Authorization": auth]
2. 正确将parameters转为JSON数据,而非字符串
你原来用"\(parameters)".data(using: .utf8)把参数转成字符串再转Data,得到的是Swift字典的描述字符串(比如["key": "value"]),这不是合法的JSON格式!服务器拿到这个"data"字段后,可能无法正确解析,即便数据最终写入,也可能返回异常响应。
正确做法是用JSONSerialization把parameters字典转成标准JSON Data:
// 替换原来的multipartFormData.append("\(parameters)"...)这行代码 if let jsonData = try? JSONSerialization.data(withJSONObject: parameters, options: []) { multipartFormData.append(jsonData, withName: "data") } else { print("参数转JSON失败") // 这里可以添加参数错误的处理逻辑 }
第三步:根据服务器返回调整响应处理逻辑
如果服务器确实不返回JSON(比如只返回"success"纯文本),那你就不该用responseJSON,直接用responseString处理即可。如果服务器应该返回JSON,那需要排查服务器端问题:
- 检查服务器返回的JSON是否有语法错误(比如多余的逗号、引号不匹配)
- 确认服务器在成功写入数据后,确实返回了合法的JSON响应
let auth = MitraModal.sharedInstance().getBasicAuthenticationString() let headers = ["Authorization": auth] // 只保留必要的Authorization头 Alamofire.upload( multipartFormData: { multipartFormData in // 正确将参数转为JSON Data if let jsonData = try? JSONSerialization.data(withJSONObject: parameters, options: []) { multipartFormData.append(jsonData, withName: "data") } // 处理图片数组 if !imageArray.isEmpty { for item in imageArray { guard let imageData = item else { continue } multipartFormData.append(imageData, withName: "file", fileName: "image.png", mimeType: "image/png") } } }, usingThreshold: UInt64.init(), to: url, method: .post, headers: headers ) { result in switch result { case .success(let upload, _, _): upload.responseString { response in print("服务器原始响应:\(response.result.value ?? "无响应")") if let err = response.error { onError?(err) return } // 尝试解析JSON(如果服务器返回JSON的话) if let responseStr = response.result.value, let data = responseStr.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON { onCompletion?(json) print("上传成功并解析JSON完成") } else { // 非JSON响应的处理逻辑 print("上传成功,但响应不是合法JSON") onCompletion?(nil) } } case .failure(let error): print("上传错误:\(error.localizedDescription)") onError?(error) } }
内容的提问来源于stack exchange,提问作者Justin Mathews

