7

I want to send an image as a parameter along with my request. I have used the code below to call my POST request but I don't know how to append image to the body.

I am getting the image through image picker as follows:

if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {

My request is formed as below

var request = URLRequest(url: URL(string: "")!) // link removed
        request.httpMethod = "POST"
        let postString = "user_id=\(userId)&image=\(image)"
        request.httpBody = postString.data(using:.utf8)

        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard let data = data, error == nil else {               // check for fundamental networking error
                return
            }
            do {
                let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? AnyObject

                if let parseJSON = json {
                    print("resp :\(parseJSON)")
                }
            } catch let error as NSError {
                print("error : \(error)")
            }
        }
        task.resume()

I am new to Swift. I have seen this through multipart/form-data but unable to implement it myself. I do not want to encode it in base 64 format. Please help me in this.

Mamta
  • 921
  • 1
  • 10
  • 28
  • Please follow this link http://stackoverflow.com/questions/16434537/post-image-to-server-in-iphone. – Mridul Gupta Dec 08 '16 at 06:11
  • Convert `Image` base64 string. https://gist.github.com/brocoo/6e7466f3138b2e5763ef. Use the implementation and not the file. – Sachin Vas Dec 08 '16 at 06:13
  • I do not want to convert it to base 64 format. I want to use it directly – Mamta Dec 08 '16 at 06:16
  • @Mridul can you please post the same code as in the link, in swift 3? The answer by RIYAZ is perfect there. – Mamta Dec 08 '16 at 06:44
  • You can check this answer doing the same as you need.. http://stackoverflow.com/a/40907477/5172413 – Krishna Datt Shukla Dec 08 '16 at 13:41
  • Possible duplicate of [How to upload images to a server in iOS with Swift?](https://stackoverflow.com/questions/26335656/how-to-upload-images-to-a-server-in-ios-with-swift) – Naresh Oct 05 '18 at 09:22

3 Answers3

20

I use the following structure for sending images:

func createRequestBodyWith(parameters:[String:NSObject], filePathKey:String, boundary:String) -> NSData {
    
    let body = NSMutableData()

    for (key, value) in parameters {
        body.appendString(string: "--\(boundary)\r\n")
        body.appendString(string: "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
        body.appendString(string: "\(value)\r\n")
    }

    body.appendString(string: "--\(boundary)\r\n")

    let mimetype = "image/jpg"

    let defFileName = "yourImageName.jpg"
        
    let imageData = UIImageJPEGRepresentation(yourImage, 1)

    body.appendString(string: "Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(defFileName)\"\r\n")
    body.appendString(string: "Content-Type: \(mimetype)\r\n\r\n")
    body.append(imageData!)
    body.appendString(string: "\r\n")

    body.appendString(string: "--\(boundary)--\r\n")

    return body
}



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



extension NSMutableData {
    func appendString(string: String) {
        let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
        append(data!)
    }
}

then you have to create body in your function like following:

request.httpBody = self.createRequestBodyWith(parameters:yourParamsDictionary, filePathKey:yourKey, boundary:self.generateBoundaryString)
gcharita
  • 7,729
  • 3
  • 20
  • 37
Elena
  • 829
  • 8
  • 20
  • What do I have to pass in this: filePathKey:yourKey? Is it the API key? – Mamta Dec 08 '16 at 08:23
  • @Dia Exactly, the API key for image parameter – Elena Dec 08 '16 at 08:32
  • Ok. My image is getting selected from imagepicker. so how can i reframe this line: let defFileName = "yourImageName.jpg"? – Mamta Dec 08 '16 at 08:46
  • @Dia Just give your image any name with .jpg extension – Elena Dec 08 '16 at 08:51
  • @Dia Welcome =) – Elena Dec 08 '16 at 08:56
  • I am still struggling with filePathKey. Can you tell me what does it need exactly? – Mamta Dec 08 '16 at 09:15
  • 1
    @Dia in your code you have this `let postString = "user_id=\(userId)&image=\(image)"`. In this case "image" is filePathKey. "filePathKey" is just a random parameter name, you can call it apiKey or whatever. – Elena Dec 08 '16 at 09:17
  • "image" is of type UIImage but the filePathKey parameter is of type String – Mamta Dec 08 '16 at 09:18
  • "image" of type UIImage is value, and "image" of type String is key – Elena Dec 08 '16 at 09:20
  • my image is fetched as follows : if let image = info[UIImagePickerControllerOriginalImage] as? UIImage so how do I make it a string? – Mamta Dec 08 '16 at 09:23
  • @Dia. This image you convert to `imageData` in my code. And 'filePathKey' is the string key for the image that you server defines. – Elena Dec 08 '16 at 09:43
  • 1
    And it is good to add Content-Type: multipart/form-data; boundary=YourBoundaryKey eg. request.addValue("multipart/form-data; boundary=" + boundaryString, forHTTPHeaderField: "Content-Type") – asdr Mar 29 '17 at 13:01
  • 1
    @Elena i want to thank you a lot, and i wounded if you may post a complete example that api can take two parameters lets say let postString = "user_id=\(userId)&access="123"&image=\(image)" – Amr Angry Apr 25 '17 at 14:21
  • i might be dumb or something... but please guys i need help what should i put in yourParamsDictionary anyone could put a sample of [String:NSobject] – Muhammad Asyraf Apr 06 '18 at 10:34
  • 1
    @Muhammad You put there any other values you want to upload to server along with the image. For instance, if you uploading a user profile image your server would probably request a user id, so yourParamsDictionary would contain something like:["userID":"55787asd9878273"] – Elena Apr 07 '18 at 17:59
  • @Elena owww, yeah thats makes sense. meaning if i need to post another parameter. got it. thanks. but i did used another method with Alamofire. i will post another answer later – Muhammad Asyraf Apr 09 '18 at 05:31
7

Swift 4.2

func uploadImage(paramName: String, fileName: String, image: UIImage) {
    let url = URL(string: "http://api-host-name/v1/api/uploadfile/single")

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

    let session = URLSession.shared

    // Set the URLRequest to POST and to the specified URL
    var urlRequest = URLRequest(url: url!)
    urlRequest.httpMethod = "POST"

    // 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
    urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

    var data = Data()

    // 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=\"\(paramName)\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!)
    data.append("Content-Type: image/png\r\n\r\n".data(using: .utf8)!)
    data.append(image.pngData()!)

    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: urlRequest, from: data, completionHandler: { responseData, response, error in
        if error == nil {
            let jsonData = try? JSONSerialization.jsonObject(with: responseData!, options: .allowFragments)
            if let json = jsonData as? [String: Any] {
                print(json)
            }
        }
    }).resume()
}

If you have any header to add, you can add it via urlRequest.setValue method.

Source: https://fluffy.es/upload-image-to-server/

Chhaileng
  • 2,428
  • 1
  • 27
  • 24
  • This is a great solution without using any libraries. Tested and works perfectly. If using PHP, access the temporarily uploaded file by `$_FILES['fileName']['tmp_name']` then copy / handle as necessary. – JonJ May 16 '19 at 15:19
  • Thanks saved my lot of time. Really appreciated. Please, can you post it on medium with the explanation. Anyways thanks for the answer. – Nex Mishra Dec 07 '19 at 12:43
  • @NexMishra Read this https://fluffy.es/upload-image-to-server/ they will explain how we upload using boundary in URLSession. – Chhaileng Dec 09 '19 at 04:53
1

Original Thread: upload image to server using Alamofire answer by Ekta Padaliya

Upload Image Using AlamoFire

  1. Installation guide please follow tutorials
    https://github.com/Alamofire/Alamofire
    Language used Swift 4

  2. Import AlamoFire Library

    import Alamofire
    
  3. Create a custom Method.
    (can paste this in UIViewController Extension / your UIViewcontroller as new function)

    func uploadImage(img: UIImage){
     let ImageData = UIImagePNGRepresentation(img)
     let urlReq = "http://apiUrl.php"
     let parameters = ["user_id": "useridValue"]//you can comment this if not needed
    
     Alamofire.upload(multipartFormData: { multipartFormData in
         multipartFormData.append(ImageData!, withName: "shop_logo",fileName: "file.jpg", mimeType: "image/jpg")
         for (key, value) in parameters {// this will loop the 'parameters' value, you can comment this if not needed
             multipartFormData.append(value.data(using: String.Encoding.utf8)!, withName: key)
         }
     },
                      to:urlReq)
     { (result) in
         switch result {
         case .success(let upload, _, _):
    
             upload.uploadProgress(closure: { (progress) in
                 print("Upload Progress: \(progress.fractionCompleted)")
             })
    
             upload.responseJSON { response in
                 print(response.result.value)
                 if let dic = response.result.value as? NSDictionary{
                     //do your action base on Api Return failed/success
                 }
             }
    
         case .failure(let encodingError):
             print(encodingError)
         }
     }
    }
    
  4. Calling the method like so

    self.uploadImage(img: image)
    
qtmfld
  • 2,916
  • 2
  • 21
  • 36
Muhammad Asyraf
  • 1,748
  • 15
  • 20