- используйте
URL
вместо NSURL var request = URLRequest
является изменяемым, используйте это вместо NSMutableURLRequest
var data = Data()
является изменяемым, используйте это вместо NSMutableData
- безопасное добавление данных файлового объекта с использованием
Data(contentsOf:options:)
метода content-type
отсутствует в параметрах, поэтому if let contentType = param["content-type"] { ... }
не удастся продолжить, используя application/octet-stream
тип MIME по умолчанию - в зависимости от сервера может потребоваться указать имя файла для загрузки
Я исправил все вышеперечисленные проблемы и переместил генерирующий код URLRequest.httpBody
в следующее расширение.
extension URLRequest {
private func formHeader(_ name: String, crlf: String, fileName: String? = nil, mimeType: String? = nil) -> String {
var str = "\(crlf)Content-Disposition: form-data; name=\"\(name)\""
guard fileName != nil || mimeType != nil else { return str + crlf + crlf }
if let name = fileName {
str += "; filename=\"\(name)\""
}
str += crlf
if let type = mimeType {
str += "Content-Type: \(type)\(crlf)"
}
return str + crlf
}
private func getFileUrl(_ file: Any) -> URL? {
if let url = file as? String {
return URL(string: url)
}
return file as? URL
}
private func getFileData(_ url: URL) -> Data? {
do {
return try Data(contentsOf: url, options: .mappedIfSafe)
} catch {
print(error)
return nil
}
}
mutating func setPost(body parameters: [[String: Any]]) {
let boundary = "Boundary+\(arc4random())\(arc4random())"
self.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var data = Data()
data.append("--\(boundary)".data(using: .utf8)!)
let crlf = "\r\n"
for parameter in parameters {
guard let paramName = parameter["name"] as? String else { continue }
if let value = parameter["value"] {
let header = formHeader(paramName, crlf: crlf)
data.append("\(header)\(value)".data(using: .utf8)!)
} else if let file = parameter["file"], let fileUrl = getFileUrl(file), let fileData = getFileData(fileUrl) {
let fileName = parameter["fileName"] as? String
let contentType = parameter["content-type"] as? String
let header = formHeader(paramName, crlf: crlf, fileName: fileName ?? fileUrl.lastPathComponent, mimeType: contentType ?? "application/octet-stream")
data.append(header.data(using: .utf8)!)
data.append(fileData)
} else {
print("\(paramName): empty or invalid value")
continue
}
data.append("\(crlf)--\(boundary)".data(using: .utf8)!)
}
data.append("--\(crlf)".data(using: .utf8)!)
self.httpBody = data
self.httpMethod = "POST"
}
}
Использование
let parameters = [
["name": "firstname", "value": "alex"],
["name": "avatar", "file": URL],
["name": "avatar", "file": "file:///", "fileName": "image.png", "content-type": "image/png"]
]
request.setPost(body: parameters)
Примечание выше в параметрах
file
клавиша представляет собой либо URL
путь к объекту или файлу. Строка. fileName: image.png
для внутреннего интерфейса, представляет имя файла.
Наконец добавьте заголовки и создайте URLSession.shared.dataTask
в качестве исходного кода.
Функция обновления-2 вместо расширения
func getParameterData(_ name: String, parameter: [String : Any]) -> Data? {
var str = "\r\nContent-Disposition: form-data; name=\"\(name)\""
if let value = parameter["value"] {
return "\(str)\r\n\r\n\(value)".data(using: .utf8)!
}
guard
let file = parameter["file"],
let url = (file is String ? URL(string: file as! String) : file as? URL)
else {
return nil
}
let data: Data
do {
data = try Data(contentsOf: url, options: .mappedIfSafe)
} catch {
print(error)
return nil
}
let fileName = (parameter["fileName"] as? String) ?? url.lastPathComponent
str += "; filename=\"\(fileName)\"\r\n"
let contentType = (parameter["content-type"] as? String) ?? "application/octet-stream"
str += "Content-Type: \(contentType)\r\n"
return (str + "\r\n").data(using: .utf8)! + data
}
func setPostRequestBody(_ request: inout URLRequest, parameters: [[String: Any]]) {
let boundary = "Boundary+\(arc4random())\(arc4random())"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var data = Data()
data.append("--\(boundary)".data(using: .utf8)!)
for parameter in parameters {
guard
let name = parameter["name"] as? String,
let value = getParameterData(name, parameter: parameter)
else {
continue
}
data.append(value)
data.append("\r\n--\(boundary)".data(using: .utf8)!)
}
data.append("--\r\n".data(using: .utf8)!)
request.httpBody = data
}
Использование-2
var request = URLRequest(url: URL(string: "myUrl")!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
setPostRequestBody(&request, parameters: [
["name": "firstname", "value": "alex"],
["name": "avatar", "file": URL object or path String]
])
let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
guard error != nil else {
print(error!.localizedDescription)
return
}
let statusCocde = (response as? HTTPURLResponse)?.statusCode
print(statusCode ?? 0)
if let data = data {
print(String(data: data, encoding: .utf8) ?? "")
}
}
dataTask.resume()