Alamofire多表单上传失败

问题描述:多表单上传时,经常性的失败,而且失败一次后面就会一值失败。

项目用的是Alamofire处理的网络请求。

通过抓包工具发现,Content-DispositionContent-Type的前面时上传会成功,顺序颠倒过来就会失败。

接下来就去查找Alamofire的源码,在MultipartFormData.swift文件中:

  • bodyHeader信息:
private func encodeHeaders(for bodyPart: BodyPart) -> Data { 
  var headerText = ""
  for (key, value) in bodyPart.headers {
    headerText += "\(key): \(value)\(EncodingCharacters.crlf)"
  }
  headerText += EncodingCharacters.crlf
  return headerText.data(using: String.Encoding.utf8, allowLossyConversion: false)!
}

看这段代码我们注意到一点,拼接成字符串的时候,是没有顺序的。该问题出现的原因就在这。Content-Disposition字段拼接到Content-Type后面时表单上传就会失败,Content-Disposition字段拼接到最前面表单上传才会成功。

个人认为这并不是前段的bug,因为 rfc标准 在这里是没有顺序要求的。

为了解决问题,暂时做了下修改:

private func encodeHeaders(for bodyPart: BodyPart) -> Data {
   var headerText = ""
   let contentDisposition = bodyPart.headers["Content-Disposition"] ?? ""
   headerText += "Content-Disposition: \(contentDisposition)\(EncodingCharacters.crlf)"
   for (key, value) in bodyPart.headers {
      if key == "Content-Disposition" { continue }
      headerText += "\(key): \(value)\(EncodingCharacters.crlf)"
   }
   headerText += EncodingCharacters.crlf
   return headerText.data(using: String.Encoding.utf8, allowLossyConversion: false)!
}

这里每次先取出Content-Disposition字段的内容拼接到前面, 这样每次上传都会成功了。

  • bodyPart信息:
    private func encode(_ bodyPart: BodyPart) throws -> Data {
        var encoded = Data()

        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
        encoded.append(initialData)

        let headerData = encodeHeaders(for: bodyPart)
        encoded.append(headerData)

        let bodyStreamData = try encodeBodyStream(for: bodyPart)
        encoded.append(bodyStreamData)

        if bodyPart.hasFinalBoundary {
            encoded.append(finalBoundaryData())
        }

        return encoded
    }

headerData数据会放到放在 bodyStreamData 前面。

再来了解下bodyStream的编码:

    private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data {
        let inputStream = bodyPart.bodyStream
        inputStream.open()
        defer { inputStream.close() }

        var encoded = Data()

        while inputStream.hasBytesAvailable {
            var buffer = [UInt8](repeating: 0, count: streamBufferSize)
            let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)

            if let error = inputStream.streamError {
                throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error))
            }

            if bytesRead > 0 {
                encoded.append(buffer, count: bytesRead)
            } else {
                break
            }
        }

        return encoded
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容