問題描述:多表單上傳時,經(jīng)常性的失敗,而且失敗一次后面就會一值失敗。
項目用的是Alamofire處理的網(wǎng)絡請求。

通過抓包工具發(fā)現(xiàn),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)!
}
看這段代碼我們注意到一點,拼接成字符串的時候,是沒有順序的。該問題出現(xiàn)的原因就在這。當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字段的內(nèi)容拼接到前面, 這樣每次上傳都會成功了。
-
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數(shù)據(jù)會放到放在 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
}