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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:50:31