Upload Image with Multipart Form-Data iOS in Swift

Upload image with multipart form-data only in Swift 4.2

Please check my solution which is taken parameters and images.I have tested with PHP, .net, java.

class func request(withImages path:APIMethods, method:URLMethod, token : String?, headers:[String:String]?, parameters: [String:Any]?,imageNames : [String], images:[Data], completion: @escaping(Any?, Error?, Bool)->Void) {


if !Reachability.isConnectedToNetwork() {
DispatchQueue.main.async {
showAlert(message: AppMessages.connectionFailed.rawValue)
}
dissmissHud()
return
}

var stringUrl = "\(APIManager.url)\(APIMethods.preFix.rawValue)\(path.rawValue)"
if method == .get, let lastPath = parameters?.values.first as? String {
stringUrl += lastPath
}else{
stringUrl += token ?? ""
}


// generate boundary string using a unique per-app string
let boundary = UUID().uuidString

let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)

print("\n\ncomplete Url :-------------- ",stringUrl," \n\n-------------: complete Url")
guard let url = URL(string: stringUrl) else { return }
var request = URLRequest(url: url)
request.httpMethod = method.rawValue

if headers != nil{
print("\n\nHeaders :-------------- ",headers as Any,"\n\n --------------: Headers")
for (key, value) in headers! {
request.setValue(value, forHTTPHeaderField: key)

}
}

// Set Content-Type Header to multipart/form-data, this is equivalent to submitting form data with file upload in a web browser
// And the boundary is also set here
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

var data = Data()
if parameters != nil{
for(key, value) in parameters!{
// Add the reqtype field and its value to the raw http request data
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
data.append("\(value)".data(using: .utf8)!)
}
}
for (index,imageData) in images.enumerated() {
// Add the image data to the raw http request data
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"\(imageNames[index])\"; filename=\"\(imageNames[index])\"\r\n".data(using: .utf8)!)
data.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
data.append(imageData)
}

// End the raw http request data, note that there is 2 extra dash ("-") at the end, this is to indicate the end of the data
data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)

// Send a POST request to the URL, with the data we created earlier
session.uploadTask(with: request, from: data, completionHandler: { data, response, error in

if let checkResponse = response as? HTTPURLResponse{
if checkResponse.statusCode == 200{
guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: [JSONSerialization.ReadingOptions.allowFragments]) else {
completion(nil, error, false)
return
}
let jsonString = String(data: data, encoding: .utf8)!
print("\n\n---------------------------\n\n"+jsonString+"\n\n---------------------------\n\n")
print(json)
completion(json, nil, true)
}else{
guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) else {
completion(nil, error, false)
return
}
let jsonString = String(data: data, encoding: .utf8)!
print("\n\n---------------------------\n\n"+jsonString+"\n\n---------------------------\n\n")
print(json)
completion(json, nil, false)
}
}else{
guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) else {
completion(nil, error, false)
return
}
completion(json, nil, false)
}

}).resume()

}

extension Data {

/// Append string to Data
///
/// Rather than littering my code with calls to `data(using: .utf8)` to convert `String` values to `Data`, this wraps it in a nice convenient little extension to Data. This defaults to converting using UTF-8.
///
/// - parameter string: The string to be added to the `Data`.

mutating func append(_ string: String, using encoding: String.Encoding = .utf8) {
if let data = string.data(using: encoding) {
append(data)
}
}
}

How to upload image file using Codable and URLSession.shared.uploadTask (multipart/form-data) in Swift?

Finally I was able to find the solution.
The source is: URLSession: Multipart Form-Data Requests | Swift 3, Xcode 8.
In my specific case I need to provide orderExtId as a parameter for the backend server to accept my image. Your case may vary, depending on the requirements of the backend.

func requestNativeImageUpload(image: UIImage, orderExtId: String) {

guard let url = imageUploadEndpoint else { return }
let boundary = generateBoundary()
var request = URLRequest(url: url)

let parameters = ["order_ext_id": orderExtId]

guard let mediaImage = Media(withImage: image, forKey: "file") else { return }

request.httpMethod = "POST"

request.allHTTPHeaderFields = [
"X-User-Agent": "ios",
"Accept-Language": "en",
"Accept": "application/json",
"Content-Type": "multipart/form-data; boundary=\(boundary)",
"ApiKey": KeychainService.getString(by: KeychainKey.apiKey) ?? ""
]

let dataBody = createDataBody(withParameters: parameters, media: [mediaImage], boundary: boundary)
request.httpBody = dataBody

let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}

if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
}


func generateBoundary() -> String {
return "Boundary-\(NSUUID().uuidString)"
}

func createDataBody(withParameters params: [String: String]?, media: [Media]?, boundary: String) -> Data {

let lineBreak = "\r\n"
var body = Data()

if let parameters = params {
for (key, value) in parameters {
body.append("--\(boundary + lineBreak)")
body.append("Content-Disposition: form-data; name=\"\(key)\"\(lineBreak + lineBreak)")
body.append("\(value + lineBreak)")
}
}

if let media = media {
for photo in media {
body.append("--\(boundary + lineBreak)")
body.append("Content-Disposition: form-data; name=\"\(photo.key)\"; filename=\"\(photo.fileName)\"\(lineBreak)")
body.append("Content-Type: \(photo.mimeType + lineBreak + lineBreak)")
body.append(photo.data)
body.append(lineBreak)
}
}

body.append("--\(boundary)--\(lineBreak)")

return body
}


extension Data {
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
append(data)
}
}
}


struct Media {
let key: String
let fileName: String
let data: Data
let mimeType: String

init?(withImage image: UIImage, forKey key: String) {
self.key = key
self.mimeType = "image/jpg"
self.fileName = "\(arc4random()).jpeg"

guard let data = image.jpegData(compressionQuality: 0.5) else { return nil }
self.data = data
}
}


Related Topics



Leave a reply



Submit