Send POST parameters with MultipartFormData using Alamofire, in iOS Swift
Asked Answered
L

13

77

I am using Alamofire, very first time. I am using the latest version Alamofire 1.3.1. I want to send one image , one video and some POST parameters in one API call. I am using multipart form data. The mutipart module is working. I am facing a problem to send extra POST parametersparams . Below is my code. "params" is the dictionary which contains extra parameters? How can I append these POST parameters in the request. Please help

        var fullUrl :String = Constants.BASE_URL + "/api/CompleteChallenge"
         var params = [
        "authKey": Constants.AuthKey,
        "idUserChallenge": "16",
        "comment": "",
        "photo": imagePath,
        "video": videoPath,
        "latitude": "1",
        "longitude": "1",
        "location": "india"
    ]

    let imagePathUrl = NSURL(fileURLWithPath: imagePath!)
    let videoPathUrl = NSURL(fileURLWithPath: videoPath!)

        Alamofire.upload(
        .POST,
        URLString: fullUrl, // http://httpbin.org/post
        multipartFormData: { multipartFormData in
            multipartFormData.appendBodyPart(fileURL: imagePathUrl!, name: "photo")
            multipartFormData.appendBodyPart(fileURL: videoPathUrl!, name: "video")
        },
        encodingCompletion: { encodingResult in
            switch encodingResult {
            case .Success(let upload, _, _):
                upload.responseJSON { request, response, JSON, error in

                  }
                }
            case .Failure(let encodingError):

            }
        }
    )
Lewislewisite answered 11/8, 2015 at 18:16 Comment(1)
possible duplicate #26122327Embed
L
85

I found the solution :) finally.

We can append data in the request as multipartformdata.

Below is my code.

  Alamofire.upload(
        .POST,
        URLString: fullUrl, // http://httpbin.org/post
        multipartFormData: { multipartFormData in
            multipartFormData.appendBodyPart(fileURL: imagePathUrl!, name: "photo")
            multipartFormData.appendBodyPart(fileURL: videoPathUrl!, name: "video")
            multipartFormData.appendBodyPart(data: Constants.AuthKey.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!, name :"authKey")
            multipartFormData.appendBodyPart(data: "\(16)".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!, name :"idUserChallenge")
            multipartFormData.appendBodyPart(data: "comment".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!, name :"comment")
            multipartFormData.appendBodyPart(data:"\(0.00)".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!, name :"latitude")
            multipartFormData.appendBodyPart(data:"\(0.00)".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!, name :"longitude")
            multipartFormData.appendBodyPart(data:"India".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!, name :"location")
        },
        encodingCompletion: { encodingResult in
            switch encodingResult {
            case .Success(let upload, _, _):
                upload.responseJSON { request, response, JSON, error in


                }
            case .Failure(let encodingError):

            }
        }
    )

EDIT 1: For those who are trying to send an array instead of float, int or string, They can convert their array or any kind of data-structure in Json String, pass this JSON string as a normal string. And parse this json string at backend to get original array

Lewislewisite answered 12/8, 2015 at 6:38 Comment(9)
If I have an array as a parameter then.Lexicologist
You will have to convert array in string. One can not send array in parameters.Lewislewisite
Okay actually my backend pHP is wanting me to send array as parameter as that API works for android. So if I send it encoding in string so I also need to have modifications in my backend pHP also....right.Lexicologist
Can I print multipartFormData so that I know what i am going to send to server ?Calcite
can anyone update it for swift 3 and Alamofire 4 please. I am having trouble converting parameter to type data when passing parameters to the append function.Naquin
@Lewislewisite please can you convert your answer into Alamofire 4.0 into swift 3?Priory
@Lewislewisite hii bro need help from thisPeer
@Lewislewisite How can i pass array into this ? As per my requirement it should not convert into string i need to pass array.Twit
@MiteshDobareeya I edited my answer. You can try that approachLewislewisite
S
41

In Alamofire 4 it is important to add the body data before you add the file data!

let parameters = [String: String]()
[...]
self.manager.upload(
    multipartFormData: { multipartFormData in
        for (key, value) in parameters {
            multipartFormData.append(value.data(using: .utf8)!, withName: key)
        }
        multipartFormData.append(imageData, withName: "user", fileName: "user.jpg", mimeType: "image/jpeg")
    },
    to: path,
    [...]
)
Sublimity answered 5/11, 2016 at 16:8 Comment(8)
how about audio file? - i try to send a audio file in this form : multipartFormData.append(audioLocalPath, withName: "file", fileName: "file", mimeType: "application/octet-stream") but occur this error : multipartEncodingFailed(Alamofire.AFError.MultipartEncodingFailureReason.bodyPartFileNotReachableWithError(file:///var/mobile/Containers/....... /Documents/item.mp3, NSUnderlyingError=0x16049100 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}})) - where is problem? -bad request or bad audio path?Elute
No such file or directory looks like a bad file pathSublimity
yes! - i know it's bad path but im try get path with way : ` let localPath = audioURL.appendingPathComponent(audioName!)` , and it work for image but not for audiosElute
Hello Alexander , i am started swift 3.0 and now i am using Almofire 4. I tried to upload image but its not working for me. Even i am not getting response from server.Thersathersites
Does the server receive the request with or without data?Sublimity
request.FILES shows nothing in django, but the body has the image info!:(Barnyard
This gave me an error: "Value of type 'Any' has no member 'data'". I just used this instead: "multipartFormData.append("\(value)".data(using: .utf8)!, withName: key)"Gall
"In Alamofire 4 it is important to add the body data before you add the file data" This line meant a lot to me. I was stuck for 2 days just because I was adding image first and then body and server was returning 400. Thanks But it didn't happened in my earlier apps using alamofire 4. This was the first time and was totally weird. Can anyone explain why? +1Posada
E
21

This is how i solve my problem

let parameters = [
            "station_id" :        "1000",
            "title":      "Murat Akdeniz",
            "body":        "xxxxxx"]

let imgData = UIImageJPEGRepresentation(UIImage(named: "1.png")!,1)



    Alamofire.upload(
        multipartFormData: { MultipartFormData in
        //    multipartFormData.append(imageData, withName: "user", fileName: "user.jpg", mimeType: "image/jpeg")

            for (key, value) in parameters {
                MultipartFormData.append(value.data(using: String.Encoding.utf8)!, withName: key)
            }

        MultipartFormData.append(UIImageJPEGRepresentation(UIImage(named: "1.png")!, 1)!, withName: "photos[1]", fileName: "swift_file.jpeg", mimeType: "image/jpeg")
        MultipartFormData.append(UIImageJPEGRepresentation(UIImage(named: "1.png")!, 1)!, withName: "photos[2]", fileName: "swift_file.jpeg", mimeType: "image/jpeg")


    }, to: "http://platform.twitone.com/station/add-feedback") { (result) in

        switch result {
        case .success(let upload, _, _):

            upload.responseJSON { response in
                print(response.result.value)
            }

        case .failure(let encodingError): break
            print(encodingError)
        }


    }
Eckman answered 22/12, 2016 at 18:48 Comment(0)
E
9

Swift 3 / Alamofire 4.0 (Addendum to the accepted answer)

To append to multipartFormData in Swift 3 / Alamofire 4.0, use the following method of MultipartFormData:

public func append(_ data: Data, withName name: String) { /* ... */ }

And, to convert String to Data, the data(using:) method of String. E.g.,

multipartFormData.append("comment".data(using: .utf8)!, withName: "comment")
Ezraezri answered 30/12, 2016 at 20:33 Comment(2)
thanks. do you have any example with a URLRequestConvertible parameter?Hodeida
I have to append Int data so what to do? Can you please help me ?Emaciation
N
9

Alamofire 5 and above

AF.upload(multipartFormData: { multipartFormData in
    multipartFormData.append(Data("one".utf8), withName: "one")
    multipartFormData.append(Data("two".utf8), withName: "two")
    multipartFormData.append(imageData, withName: "file", fileName: "photo.jpg", mimeType: "image/jpg")
}, 
to: "https://httpbin.org/post").responseDecodable(of: MultipartResponse.self) { response in
        debugPrint(response)
}

documentation link: multipart upload

Nato answered 4/8, 2020 at 14:44 Comment(2)
What is MultipartResponse ?Implead
This is the latest solution, some answers are really outdated. The current major Alamofire is 5. Should use this over other solutions.Rackrent
B
6

For Swift 4.2 / Alamofire 4.7.3

Alamofire.upload(multipartFormData: { multipart in
    multipart.append(fileData, withName: "payload", fileName: "someFile.jpg", mimeType: "image/jpeg")
    multipart.append("comment".data(using: .utf8)!, withName :"comment")
}, to: "endPointURL", method: .post, headers: nil) { encodingResult in
    switch encodingResult {
    case .success(let upload, _, _):
        upload.response { answer in
            print("statusCode: \(answer.response?.statusCode)")
        }
        upload.uploadProgress { progress in
            //call progress callback here if you need it
        }
    case .failure(let encodingError):
        print("multipart upload encodingError: \(encodingError)")
    }
}

Also you could take a look at CodyFire lib it makes API calls easier using Codable for everything. Example for Multipart call using CodyFire

//Declare your multipart payload model
struct MyPayload: MultipartPayload {
    var attachment: Attachment //or you could use just Data instead
    var comment: String
}

// Prepare payload for request
let imageAttachment = Attachment(data: UIImage(named: "cat")!.jpeg(.high)!,
                                 fileName: "cat.jpg",
                                 mimeType: .jpg)
let payload = MyPayload(attachment: imageAttachment, comment: "Some text")

//Send request easily
APIRequest("endpoint", payload: payload)
    .method(.post)
    .desiredStatus(.created) //201 CREATED
    .onError { error in
        switch error.code {
        case .notFound: print("Not found")
        default: print("Another error: " + error.description)
        }
    }.onSuccess { result in
        print("here is your decoded result")
    }
//Btw normally it should be wrapped into an extension
//so it should look even easier API.some.upload(payload).onError{}.onSuccess{}

You could take a look at all the examples in lib's readme

Boron answered 29/9, 2018 at 7:39 Comment(0)
G
5

Swift 5, update @Ankush's Alamofire Code to

     var fullUrl = "http://httpbin.org/post" // for example

           Alamofire.upload(multipartFormData: { (multipartFormData) in
                multipartFormData.append( imagePathUrl! , withName: "photo")
                multipartFormData.append( videoPathUrl!,  withName: "video")
                multipartFormData.append(Constants.AuthKey.data(using: .utf8, allowLossyConversion: false)!, withName: "authKey")
                multipartFormData.append("16".data(using: .utf8, allowLossyConversion: false)!, withName: "idUserChallenge")
                multipartFormData.append("111".data(using: .utf8, allowLossyConversion: false)!, withName: "authKey")
                multipartFormData.append("comment".data(using: .utf8, allowLossyConversion: false)!, withName: "comment")
                multipartFormData.append("0.00".data(using: .utf8, allowLossyConversion: false)!, withName: "latitude")
                multipartFormData.append("0.00".data(using: .utf8, allowLossyConversion: false)!, withName: "longitude")
                multipartFormData.append("India".data(using: .utf8, allowLossyConversion: false)!, withName: "location")

            }, to: fullUrl, method: .post) { (encodingResult) in
                switch encodingResult {
                case .success(request: let upload, streamingFromDisk: _, streamFileURL: _):
                    upload.responseJSON { (response) in   // do sth     }
                case .failure(let encodingError):
                    ()
                }
            }
Ghoul answered 16/6, 2020 at 11:19 Comment(0)
G
3

As in Swift 3.x for upload image with parameter we can use below alamofire upload method-

static func uploadImageData(inputUrl:String,parameters:[String:Any],imageName: String,imageFile : UIImage,completion:@escaping(_:Any)->Void) {

        let imageData = UIImageJPEGRepresentation(imageFile , 0.5)

        Alamofire.upload(multipartFormData: { (multipartFormData) in

            multipartFormData.append(imageData!, withName: imageName, fileName: "swift_file\(arc4random_uniform(100)).jpeg", mimeType: "image/jpeg")

            for key in parameters.keys{
                let name = String(key)
                if let val = parameters[name!] as? String{
                    multipartFormData.append(val.data(using: .utf8)!, withName: name!)
                }
            }
        }, to:inputUrl)
        { (result) in
            switch result {
            case .success(let upload, _, _):

                upload.uploadProgress(closure: { (Progress) in
                })

                upload.responseJSON { response in

                    if let JSON = response.result.value {
                        completion(JSON)
                    }else{
                        completion(nilValue)
                    }
                }

            case .failure(let encodingError):
                completion(nilValue)
            }

        }

    }

Note: Additionally if our parameter is array of key-pairs then we can use

 var arrayOfKeyPairs = [[String:Any]]()
 let json = try? JSONSerialization.data(withJSONObject: arrayOfKeyPairs, options: [.prettyPrinted])
 let jsonPresentation = String(data: json!, encoding: .utf8)
Gamut answered 21/7, 2017 at 2:59 Comment(0)
F
3

Well, since Multipart Form Data is intended to be used for binary ( and not for text) data transmission, I believe it's bad practice to send data in encoded to String over it.

Another disadvantage is impossibility to send more complex parameters like JSON.

That said, a better option would be to send all data in binary form, that is as Data.

Say I need to send this data

let name = "Arthur"
let userIDs = [1,2,3]
let usedAge = 20

...alongside with user's picture:

let image = UIImage(named: "img")!

For that I would convert that text data to JSON and then to binary alongside with image:

//Convert image to binary
let data = UIImagePNGRepresentation(image)!

//Convert text data to binary
let dict: Dictionary<String, Any> = ["name": name, "userIDs": userIDs, "usedAge": usedAge]
userData = try? JSONSerialization.data(withJSONObject: dict)

And then, finally send it via Multipart Form Data request:

Alamofire.upload(multipartFormData: { (multiFoormData) in
        multiFoormData.append(userData, withName: "user")
        multiFoormData.append(data, withName: "picture", mimeType: "image/png")
    }, to: url) { (encodingResult) in
        ...
    }
Footloose answered 2/3, 2018 at 10:17 Comment(0)
V
1

for alamofire 4 use this ..

        Alamofire.upload(multipartFormData: { (multipartFormData) in

            multipartFormData.append(fileUrl, withName: "video")
       //fileUrl is your file path in iOS device and withName is parameter name

        }, to:"http://to_your_url_path")
        { (result) in
            switch result {
            case .success(let upload, _ , _):

                upload.uploadProgress(closure: { (progress) in

                    print("uploding")
                })

                upload.responseJSON { response in

                   print("done")

                }

            case .failure(let encodingError):
                print("failed")
                print(encodingError)

            }
        }
Visa answered 17/3, 2017 at 6:29 Comment(0)
B
0

Found one more way of doing it

 if let parameters = route.parameters {

                    for (key, value) in parameters {
                         if value is String {
                            if let temp = value as? String {
                                multipartFormData.append(temp.description.data(using: .utf8)!, withName: key)
                            }
                        }
                        else if value is NSArray {
                            if let temp = value as? [Double]{
                                multipartFormData.append(temp.description.data(using: .utf8)!, withName: key)
                            }
                            else if let temp = value as? [Int]{
                                multipartFormData.append(temp.description.data(using: .utf8)!, withName: key)
                            }
                            else if let temp = value as? [String]{
                                multipartFormData.append(temp.description.data(using: .utf8)!, withName: key)
                            }
                        }
                        else if CFGetTypeID(value as CFTypeRef) == CFNumberGetTypeID() {
                            if let temp = value as? Int {
                                multipartFormData.append(temp.description.data(using: .utf8)!, withName: key)
                            }
                        }
                        else if CFGetTypeID(value as CFTypeRef) == CFBooleanGetTypeID(){
                            if let temp = value as? Bool {
                                multipartFormData.append(temp.description.data(using: .utf8)!, withName: key)
                            }
                        }
                    }
                }

                if let items: [MultipartData] = route.multipartData{
                    for item in items {
                        if let value = item.value{
                            multipartFormData.append(value, withName: item.key, fileName: item.fileName, mimeType: item.mimeType)
                        }
                    }
                }
Bloater answered 6/1, 2020 at 11:50 Comment(0)
W
0

Alamofire 5 with Array params

The issue for me was that my params could be any type. Converting to them to strings and then getting the data encoding worked for most, but I kept running into issues with arrays. For arrays you have to encode each element with a key denoting its index. This is how I ended up encoding my params so it would be dynamic for any type:

            let image = UIImage(named: "your image") // Change me
            let imageData = image.jpegData(compressionQuality: 1.0)!
            let imageKey = "image_key" // Change me
            let urlString = "https://yourserver.com/yourendpoint/" // Change me
            let params: [String: Any]? = [:] // Change me. Your POST params here
            let headers: HTTPHeaders = [
                // Change me. Your headers here.
            ]
            AF.upload(multipartFormData: { multiPart in
                for (key, value) in (params ?? [:]) {
                    if let arrayObj = value as? [Any] {
                        for index in 0..<arrayObj.count {
                            multiPart.append("\(arrayObj[index])".data(using: .utf8)!, withName: "\(key)[\(index)]")
                        }
                    } else {
                        multiPart.append("\(value)".data(using: .utf8)!, withName: key)
                    }
                }
                multiPart.append(imageData, withName: imageKey, fileName: "file.jpg", mimeType: "image/jpg")
            }, to: urlString, headers: headers).responseJSON { response in
                switch response.result {
                case .success(_):
                    if let dictionary = response.value as? [String:Any] {
                        print("success", dictionary)
                    } else {
                        print("error")
                    }
                case .failure(let error):
                    print("error", error.localizedDescription)
                }
            }

Also note that you have to append the image after appending all your params

Weasand answered 3/11, 2022 at 14:22 Comment(0)
S
-1
 func savePostWithImage(title: String, content: String, image: UIImage){
    let parameters: Parameters = [
        "title":    title,
        "content":  content
    ]
    
    guard let url = URL(string: "http://localhost/proyecto/crud/save.php") else { return }
    
    guard let imgData = image.pngData() else { return }
    
    let imageId = UUID().uuidString
    
    DispatchQueue.main.async {
        AF.upload(multipartFormData: { MultipartFormData in
            MultipartFormData.append(imgData, withName: "Image", fileName: "\(imageId).png", mimeType: "image/png")
            
            for (key, value) in parameters {
                MultipartFormData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
            }
            
        }, to: url, method: .post).uploadProgress { Progress in
            print(Progress.fractionCompleted * 100)
        }.response { [self] response in
            createdPostAlertMessage = successCreatingPostMessage
            showReponseAlert = true
        }
    }
    
}
Stanfill answered 3/7, 2023 at 1:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.