Alamofire多表单上传失败

2020-08-10  本文已影响0人  Maji1

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

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

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

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

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字段的内容拼接到前面, 这样每次上传都会成功了。

    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
    }
上一篇下一篇

猜你喜欢

热点阅读