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

Swift 5.7多表单数据图片上传异常:3张图请求无响应

Swift 5.7多图上传请求失败排查与解决建议

我在Swift 5.7环境下,尝试通过multipart/form-data请求向服务器发送3张图片及其他附加数据,但仅移除后两张图片时,请求才能正常触达服务器。以下是相关代码片段,寻求问题排查方向,实现3张图片同请求上传。

相关代码

Service.swift

let networker = Networker()
let serverUrl = URL(string: "https://myurl.com")
var urlRequest = URLRequest(url: serverUrl!)

let boundary = UUID().uuidString

urlRequest.httpMethod = "POST"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
urlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")

let requestData = networker.buildMultipartImageRequest(boundary: boundary, item: items)

URLSession.shared.uploadTask(with: urlRequest, from: requestData) {(data, response, error) in
    // 处理响应
}.resume()

Networker.swift

func buildMultipartImageRequest(boundary: String, item: ItemForBackend, itemId:UInt = 0) -> Data {
    var data = Data()
    
    // 添加上传图片数据
    data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
    data.append("Content-Disposition: form-data; name=\"images\"; filename=\"\(item.filename1)\"\r\n".data(using: .utf8)!)
    data.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
    data.append(item.image1)

//    取消注释这两段后,请求完全无法发送!
//        data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
//        data.append("Content-Disposition: form-data; name=\"images\"; filename=\"\(item.filename2)\"\r\n".data(using: .utf8)!)
//        data.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
//        data.append(item.image2)
//        
//        data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
//        data.append("Content-Disposition: form-data; name=\"images\"; filename=\"\(item.filename3)\"\r\n".data(using: .utf8)!)
//        data.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
//        data.append(item.image3)
    
    // 添加附加参数
    let paramObj:[String: Any] = item.getParams()
    
    for(key, value) in paramObj {
        data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
        data.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
        data.append("\(value)\r\n".data(using: .utf8)!)
    }
    
    if itemId != 0 {
        data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
        data.append("Content-Disposition: form-data; name=\"itemId\"\r\n\r\n".data(using: .utf8)!)
        data.append("\(itemId)\r\n".data(using: .utf8)!)
    }
    
    data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
    
    return data
}

排查方向与解决建议

  • 检查请求数据大小限制:三张图片总大小可能超过服务器配置的最大请求体限制(比如Nginx的client_max_body_size),或者触发了客户端内存预警。可以打印requestData.count对比单图和三图时的数据量,同时联系后端确认服务器的大小限制。
  • 修正multipart字段命名格式:部分后端框架(如PHP)要求多文件字段名带[]后缀才能识别,把图片字段的name=\"images\"改成name=\"images[]\"试试。
  • 验证图片数据有效性:检查image2和image3是否为完整有效的JPEG数据,打印它们的字节数确认数据长度合理,避免空数据或损坏数据导致请求格式错误。
  • 改用OutputStream构建请求数据:手动拼接大Data容易出现内存溢出或隐式拼接错误,用OutputStream流式构建数据更稳定,示例代码如下:
func buildMultipartImageRequest(boundary: String, item: ItemForBackend, itemId: UInt = 0) -> Data {
    let outputStream = OutputStream(toMemory: ())
    outputStream.open()
    
    func write(_ string: String) {
        if let data = string.data(using: .utf8) {
            data.withUnsafeBytes { buffer in
                outputStream.write(buffer.baseAddress!.assumingMemoryBound(to: UInt8.self), maxLength: data.count)
            }
        }
    }
    
    // 第一张图
    write("\r\n--\(boundary)\r\n")
    write("Content-Disposition: form-data; name=\"images[]\"; filename=\"\(item.filename1)\"\r\n")
    write("Content-Type: image/jpeg\r\n\r\n")
    item.image1.withUnsafeBytes { buffer in
        outputStream.write(buffer.baseAddress!.assumingMemoryBound(to: UInt8.self), maxLength: item.image1.count)
    }
    
    // 第二张图
    write("\r\n--\(boundary)\r\n")
    write("Content-Disposition: form-data; name=\"images[]\"; filename=\"\(item.filename2)\"\r\n")
    write("Content-Type: image/jpeg\r\n\r\n")
    item.image2.withUnsafeBytes { buffer in
        outputStream.write(buffer.baseAddress!.assumingMemoryBound(to: UInt8.self), maxLength: item.image2.count)
    }
    
    // 第三张图
    write("\r\n--\(boundary)\r\n")
    write("Content-Disposition: form-data; name=\"images[]\"; filename=\"\(item.filename3)\"\r\n")
    write("Content-Type: image/jpeg\r\n\r\n")
    item.image3.withUnsafeBytes { buffer in
        outputStream.write(buffer.baseAddress!.assumingMemoryBound(to: UInt8.self), maxLength: item.image3.count)
    }
    
    // 附加参数
    let paramObj = item.getParams()
    for (key, value) in paramObj {
        write("\r\n--\(boundary)\r\n")
        write("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
        write("\(value)\r\n")
    }
    
    if itemId != 0 {
        write("\r\n--\(boundary)\r\n")
        write("Content-Disposition: form-data; name=\"itemId\"\r\n\r\n")
        write("\(itemId)\r\n")
    }
    
    write("\r\n--\(boundary)--\r\n")
    
    outputStream.close()
    return outputStream.property(forKey: .dataWrittenToMemoryStreamKey) as! Data
}
  • 更换URLSession上传方式:尝试用dataTask(with:request, completionHandler:)替代uploadTask,同时打印error信息,确认请求失败的具体原因。
  • 确认服务器端接收逻辑:联系后端开发人员,确认服务器是否支持接收多个同名字段的文件,是否需要调整后端的文件接收配置。

内容的提问来源于stack exchange,提问作者Tank12

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:32:10