我的 MultiPartFormData 编码器有什么问题? Swift 4
What is wrong with my MultiPartFormData Encoder? Swift 4
你好)这是我的MultiPartFormDataEncoder:
let boundary = "Boundary-\(UUID().uuidString)"
var body = ""
if let parameters = parameters {
for (key, value) in parameters {
body += "--\(boundary)\r\n"
body += "Content-Disposition:form-data; name=\"\(String(describing: key))\"\r\n\r\n"
body += "\(String(describing: value))\r\n"
}
}
if let files = files {
for file in files {
body += "--\(boundary)\r\n"
body += "Content-Disposition: form-data; name=\"\(file.key)\"; filename=\"\(file.name)\"\r\n"
body += "Content-Type: \(file.type)\r\n\r\n"
body += "\(file.data)\r\n"
}
}
body += "--".appending(boundary.appending("--"))
urlRequest.httpBody = body.data(using: .utf8)
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("multipart/form-data; charset=utf8; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
}
urlRequest.timeoutInterval = 60
urlRequest.httpShouldHandleCookies = false
向服务器发送请求后,我得到 "Bad Request"。当我尝试在邮递员请求中这样做时,成功了。
所有参数和文件都有正确的名称、类型和键。我检查了一个肯定。
所以问题不在 BackEnd 中,因为它收到我的请求并看到正确的 body 但它在获取后中断。
我认为 headers 有问题。
那么,你怎么看?
编辑:
正确响应解析后,我看到下一条消息:
{"image":["Upload a valid image. The file you uploaded was either not an image or a corrupted image."]}
所以,我找到了问题和解决方案。
这一行有错误:
body += "\(file.data)\r\n"
正如@Larme所说,我们不能将数据和字符串放在一行中。
更好的解决方案将数据和字符串分开添加,例如:
// old body += "\(file.data)\r\n"
// new:
var body: Data = Data()
body.append(file.data)
body.append("\r\n".data(using: .utf8)!)
在我的例子中,MultiPartFormDataParameterEncoder 看起来像:
func encode(urlRequest: inout URLRequest, with parameters: Parameters?, files: Files?) throws {
do {
let boundary = "Boundary-\(UUID().uuidString)"
var body: Data = Data()
if let parameters = parameters {
for (key, value) in parameters {
try body.appendString("--\(boundary)\r\n")
try body.appendString("Content-Disposition:form-data; name=\"\(String(describing: key))\"\r\n\r\n")
try body.appendString("\(String(describing: value))\r\n")
}
}
if let files = files {
for file in files {
try body.appendString("--\(boundary)\r\n")
try body.appendString("Content-Disposition: form-data; name=\"\(file.key)\"; filename=\"\(file.name)\"\r\n")
try body.appendString("Content-Type: \"\(file.type)\"\r\n\r\n")
body.append(file.data)
try body.appendString("\r\n")
}
}
try body.appendString("--".appending(boundary.appending("--")))
urlRequest.httpBody = body
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
}
} catch {
throw NetworkError.encodingMultiPartFormDataFailed
}
}
public enum NetworkError: String, Error {
case encodingMultiPartFormDataFailed = "Parameter encoding in MultiPartFormData failed ..."
}
还必须添加文件和参数模型:
public typealias Parameters = [String:Any]
public typealias Files = [FileModel]
public struct FileModel {
let name: String
let key: String
let data: Data
let type: String
init(name: String, type: FileType, data: Data) {
self.name = name
self.key = type.key // will be image or audio
self.data = data
self.type = type.rawValue // will be "audio/mp3" or "image/jpeg"
}
}
extension FileModel {
enum FileType: String {
case image = "image/jpeg"
case audio = "audio/mp3"
var key: String {
return String(describing: self)
}
}
}
// USAGE EXAMPLE
FileModel(name: name, type: .image, data: imageJpegData)
也不要忘记数据扩展以提高安全性:
extension Data {
private enum DataError: String, Error {
case encodingStringToDataFailed = "Encoding String to Data failed ..."
}
mutating func appendString(_ string: String) throws {
guard let data: Data = string.data(using: String.Encoding.utf8, allowLossyConversion: false) else {
throw DataError.encodingStringToDataFailed
}
append(data)
}
}
谢谢^_^
你好)这是我的MultiPartFormDataEncoder:
let boundary = "Boundary-\(UUID().uuidString)"
var body = ""
if let parameters = parameters {
for (key, value) in parameters {
body += "--\(boundary)\r\n"
body += "Content-Disposition:form-data; name=\"\(String(describing: key))\"\r\n\r\n"
body += "\(String(describing: value))\r\n"
}
}
if let files = files {
for file in files {
body += "--\(boundary)\r\n"
body += "Content-Disposition: form-data; name=\"\(file.key)\"; filename=\"\(file.name)\"\r\n"
body += "Content-Type: \(file.type)\r\n\r\n"
body += "\(file.data)\r\n"
}
}
body += "--".appending(boundary.appending("--"))
urlRequest.httpBody = body.data(using: .utf8)
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("multipart/form-data; charset=utf8; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
}
urlRequest.timeoutInterval = 60
urlRequest.httpShouldHandleCookies = false
向服务器发送请求后,我得到 "Bad Request"。当我尝试在邮递员请求中这样做时,成功了。 所有参数和文件都有正确的名称、类型和键。我检查了一个肯定。 所以问题不在 BackEnd 中,因为它收到我的请求并看到正确的 body 但它在获取后中断。
我认为 headers 有问题。
那么,你怎么看?
编辑: 正确响应解析后,我看到下一条消息:
{"image":["Upload a valid image. The file you uploaded was either not an image or a corrupted image."]}
所以,我找到了问题和解决方案。
这一行有错误:
body += "\(file.data)\r\n"
正如@Larme所说,我们不能将数据和字符串放在一行中。
更好的解决方案将数据和字符串分开添加,例如:
// old body += "\(file.data)\r\n"
// new:
var body: Data = Data()
body.append(file.data)
body.append("\r\n".data(using: .utf8)!)
在我的例子中,MultiPartFormDataParameterEncoder 看起来像:
func encode(urlRequest: inout URLRequest, with parameters: Parameters?, files: Files?) throws {
do {
let boundary = "Boundary-\(UUID().uuidString)"
var body: Data = Data()
if let parameters = parameters {
for (key, value) in parameters {
try body.appendString("--\(boundary)\r\n")
try body.appendString("Content-Disposition:form-data; name=\"\(String(describing: key))\"\r\n\r\n")
try body.appendString("\(String(describing: value))\r\n")
}
}
if let files = files {
for file in files {
try body.appendString("--\(boundary)\r\n")
try body.appendString("Content-Disposition: form-data; name=\"\(file.key)\"; filename=\"\(file.name)\"\r\n")
try body.appendString("Content-Type: \"\(file.type)\"\r\n\r\n")
body.append(file.data)
try body.appendString("\r\n")
}
}
try body.appendString("--".appending(boundary.appending("--")))
urlRequest.httpBody = body
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
}
} catch {
throw NetworkError.encodingMultiPartFormDataFailed
}
}
public enum NetworkError: String, Error {
case encodingMultiPartFormDataFailed = "Parameter encoding in MultiPartFormData failed ..."
}
还必须添加文件和参数模型:
public typealias Parameters = [String:Any]
public typealias Files = [FileModel]
public struct FileModel {
let name: String
let key: String
let data: Data
let type: String
init(name: String, type: FileType, data: Data) {
self.name = name
self.key = type.key // will be image or audio
self.data = data
self.type = type.rawValue // will be "audio/mp3" or "image/jpeg"
}
}
extension FileModel {
enum FileType: String {
case image = "image/jpeg"
case audio = "audio/mp3"
var key: String {
return String(describing: self)
}
}
}
// USAGE EXAMPLE
FileModel(name: name, type: .image, data: imageJpegData)
也不要忘记数据扩展以提高安全性:
extension Data {
private enum DataError: String, Error {
case encodingStringToDataFailed = "Encoding String to Data failed ..."
}
mutating func appendString(_ string: String) throws {
guard let data: Data = string.data(using: String.Encoding.utf8, allowLossyConversion: false) else {
throw DataError.encodingStringToDataFailed
}
append(data)
}
}
谢谢^_^