问题描述:多表单上传时,经常性的失败,而且失败一次后面就会一值失败。
项目用的是Alamofire
处理的网络请求。
通过抓包工具发现,Content-Disposition
在Content-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
}
网友评论