Upload image with multipart form-data iOS in Swift
Solution 1
No Need to use any library for upload images using multipart request.
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/
Solution 2
My version that 100% works. Maybe it will help you.
let url = "http://server/upload"
let img = UIImage(contentsOfFile: fullPath)
let data: NSData = UIImageJPEGRepresentation(img, 1)
sendFile(url,
fileName:"one.jpg",
data:data,
completionHandler: completionHandler:{
(result:Bool, isNoInternetConnection:Bool) -> Void in
// ...
NSLog("Complete: \(result)")
}
)
func sendFile(
urlPath:String,
fileName:String,
data:NSData,
completionHandler: (NSURLResponse!, NSData!, NSError!) -> Void){
var url: NSURL = NSURL(string: urlPath)!
var request1: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request1.HTTPMethod = "POST"
let boundary = generateBoundary()
let fullData = photoDataToFormData(data,boundary:boundary,fileName:fileName)
request1.setValue("multipart/form-data; boundary=" + boundary,
forHTTPHeaderField: "Content-Type")
// REQUIRED!
request1.setValue(String(fullData.length), forHTTPHeaderField: "Content-Length")
request1.HTTPBody = fullData
request1.HTTPShouldHandleCookies = false
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(
request1,
queue: queue,
completionHandler:completionHandler)
}
// this is a very verbose version of that function
// you can shorten it, but i left it as-is for clarity
// and as an example
func photoDataToFormData(data:NSData,boundary:String,fileName:String) -> NSData {
var fullData = NSMutableData()
// 1 - Boundary should start with --
let lineOne = "--" + boundary + "\r\n"
fullData.appendData(lineOne.dataUsingEncoding(
NSUTF8StringEncoding,
allowLossyConversion: false)!)
// 2
let lineTwo = "Content-Disposition: form-data; name=\"image\"; filename=\"" + fileName + "\"\r\n"
NSLog(lineTwo)
fullData.appendData(lineTwo.dataUsingEncoding(
NSUTF8StringEncoding,
allowLossyConversion: false)!)
// 3
let lineThree = "Content-Type: image/jpeg\r\n\r\n"
fullData.appendData(lineThree.dataUsingEncoding(
NSUTF8StringEncoding,
allowLossyConversion: false)!)
// 4
fullData.appendData(data)
// 5
let lineFive = "\r\n"
fullData.appendData(lineFive.dataUsingEncoding(
NSUTF8StringEncoding,
allowLossyConversion: false)!)
// 6 - The end. Notice -- at the start and at the end
let lineSix = "--" + boundary + "--\r\n"
fullData.appendData(lineSix.dataUsingEncoding(
NSUTF8StringEncoding,
allowLossyConversion: false)!)
return fullData
}
Solution 3
public func UPLOADIMG(url: String,parameters: Dictionary<String,AnyObject>?,filename:String,image:UIImage, success:((NSDictionary) -> Void)!, failed:((NSDictionary) -> Void)!, errord:((NSError) -> Void)!) {
var TWITTERFON_FORM_BOUNDARY:String = "AaB03x"
let url = NSURL(string: url)!
var request:NSMutableURLRequest = NSMutableURLRequest(URL: url, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData, timeoutInterval: 10)
var MPboundary:String = "--\(TWITTERFON_FORM_BOUNDARY)"
var endMPboundary:String = "\(MPboundary)--"
//convert UIImage to NSData
var data:NSData = UIImagePNGRepresentation(image)
var body:NSMutableString = NSMutableString();
// with other params
if parameters != nil {
for (key, value) in parameters! {
body.appendFormat("\(MPboundary)\r\n")
body.appendFormat("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendFormat("\(value)\r\n")
}
}
// set upload image, name is the key of image
body.appendFormat("%@\r\n",MPboundary)
body.appendFormat("Content-Disposition: form-data; name=\"\(filename)\"; filename=\"pen111.png\"\r\n")
body.appendFormat("Content-Type: image/png\r\n\r\n")
var end:String = "\r\n\(endMPboundary)"
var myRequestData:NSMutableData = NSMutableData();
myRequestData.appendData(body.dataUsingEncoding(NSUTF8StringEncoding)!)
myRequestData.appendData(data)
myRequestData.appendData(end.dataUsingEncoding(NSUTF8StringEncoding)!)
var content:String = "multipart/form-data; boundary=\(TWITTERFON_FORM_BOUNDARY)"
request.setValue(content, forHTTPHeaderField: "Content-Type")
request.setValue("\(myRequestData.length)", forHTTPHeaderField: "Content-Length")
request.HTTPBody = myRequestData
request.HTTPMethod = "POST"
// var conn:NSURLConnection = NSURLConnection(request: request, delegate: self)!
let task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {
data, response, error in
if error != nil {
println(error)
errord(error)
return
}
var parseError: NSError?
let responseObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError)
if let responseDictionary = responseObject as? NSDictionary {
success(responseDictionary)
} else {
}
})
task.resume()
}
Solution 4
import Foundation
struct MultipartFormDataRequest {
private let boundary: String = UUID().uuidString
var httpBody = NSMutableData()
let url: URL
init(url: URL) {
self.url = url
}
func addTextField(named name: String, value: String) {
httpBody.appendString(textFormField(named: name, value: value))
}
private func textFormField(named name: String, value: String) -> String {
var fieldString = "--\(boundary)\r\n"
fieldString += "Content-Disposition: form-data; name=\"\(name)\"\r\n"
fieldString += "Content-Type: text/plain; charset=ISO-8859-1\r\n"
fieldString += "Content-Transfer-Encoding: 8bit\r\n"
fieldString += "\r\n"
fieldString += "\(value)\r\n"
return fieldString
}
func addDataField(fieldName: String, fileName: String, data: Data, mimeType: String) {
httpBody.append(dataFormField(fieldName: fieldName,fileName:fileName,data: data, mimeType: mimeType))
}
private func dataFormField(fieldName: String,
fileName: String,
data: Data,
mimeType: String) -> Data {
let fieldData = NSMutableData()
fieldData.appendString("--\(boundary)\r\n")
fieldData.appendString("Content-Disposition: form-data; name=\"\(fieldName)\"; filename=\"\(fileName)\"\r\n")
fieldData.appendString("Content-Type: \(mimeType)\r\n")
fieldData.appendString("\r\n")
fieldData.append(data)
fieldData.appendString("\r\n")
return fieldData as Data
}
func asURLRequest() -> URLRequest {
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
httpBody.appendString("--\(boundary)--")
request.httpBody = httpBody as Data
return request
}
}
extension NSMutableData {
func appendString(_ string: String) {
if let data = string.data(using: .utf8) {
self.append(data)
}
}
}
extension URLSession {
func dataTask(with request: MultipartFormDataRequest,
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void)
-> URLSessionDataTask {
return dataTask(with: request.asURLRequest(), completionHandler: completionHandler)
}
}
Use this function to call upload file func
func uploadFile(file:Data, fileName: String, fileExtension: String){
var mimeType = "image/png"
if fileExtension == "PDF" {
mimeType = "application/pdf"
}
let url = "https://v2.convertapi.com/upload"
let request = MultipartFormDataRequest(url: URL(string: url)!)
request.addDataField(fieldName: "file", fileName: fileName, data: file, mimeType: mimeType)
URLSession.shared.dataTask(with: request, completionHandler: {data,urlResponse,error in
}).resume()
}
#Resources:
https://www.donnywals.com/uploading-images-and-forms-to-a-server-using-urlsession/ https://orjpap.github.io/swift/http/ios/urlsession/2021/04/26/Multipart-Form-Requests.html
Solution 5
I suggest this repository.
You can add pod 'MultipartForm'
in the Podfile and then follow the example in the repository's readme:
import MultipartForm
let form = MultipartForm(parts: [
MultipartForm.Part(name: "a", value: "1"),
MultipartForm.Part(name: "b", value: "2"),
MultipartForm.Part(name: "c", data: imageData, filename: "3.png", contentType: "image/png"),
])
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue(form.contentType, forHTTPHeaderField: "Content-Type")
let task = session.uploadTask(with: request, from: form.bodyData)
task.resume()
It supports Swift Package Manager too.
Amr Mohamed
iOS software Engineer with 5 years experience in crafting the best user interface, I am constantly passionate about learning new technologies and always looking forward to make my self better. I keep reading books every day about iOS Development and software engineering, I learned most of my experience from reading raywenderlich books and also from reading Apple programming guidelines .
Updated on July 09, 2022Comments
-
Amr Mohamed almost 2 years
i am having a problem with uploading image with multipart-form
here is my code i used from this answer
var request = NSMutableURLRequest(URL: url!) request.HTTPMethod = "POST" var boundary = generateBoundaryString() request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") var body = NSMutableData() if self.img.image != nil { var imageData = UIImagePNGRepresentation(self.img.image) if imageData != nil { body.appendString("--\(boundary)\r\n") body.appendString("Content-Disposition: form-data; name=\"image\"; filename=\"image.png\"\r\n") body.appendString("Content-Type: image/png\r\n\r\n") body.appendData(imageData!) body.appendString("\r\n") } } body.appendString("--\(boundary)--\r\n") request.setValue("\(body.length)", forHTTPHeaderField:"Content-Length") request.HTTPBody = body
then i use NSURLSession to apply the request
the server says that i didn't choose image to upload i only want to upload the image for now
do i have to use paths of images to upload any image or it's data is enough?
do i miss any thing , any help to understand this ?
-
Amr Mohamed about 9 yearsSorry for late reply and thanks for trying to help , i updated the code with
content-Type
and theContent-Length
but still got the same error , do you recommend anything else ? and also something that i don't understand is there any key i use from the php code likeform-name
or something else , is this line okbody.appendString("Content-Disposition: form-data; name=\"image\"; filename=\"image.png\"\r\n")
? -
Amr Mohamed about 9 yearssame error , do you have any other explanation ? , and what is this
name
andfilename
here in this linebody.appendFormat("Content-Disposition: form-data; name=\"\(filename)\"; filename=\"pen111.png\"\r\n")
, is there any key i use from the php code like form-name or something else in the same line , thanks -
bontoJR about 9 yearsYes, name=\"image\" is actually containing the field name you are uploading the image on. So, if in your form you have the image field named 'image-field', you have to write name=\"image-field\"
-
jansma about 9 yearsname is the key of image ,like the attribute called name in input tag(html),it is important, if you get the upload file in php use
$photo = $_FILES['file']
(thinkphp); you must set the namefile
; filename is the image file name,like "aaaa.png" you can change the name by yourself @Panda -
ajjnix about 8 yearsthanks you for with answer!) I just add in solution request1.setValue("gzip, deflate", forHTTPHeaderField: "Accept-Encoding") and my image did uploaded
-
Eric Aya over 7 years@jansma Thank you for this comment about the "name" parameter! You helped me understand why a specific API was refusing my data. Cheers.
-
Fattie about 7 yearsWow. Something I'm thinking, what if you ALSO have to send text input fields?
-
Priyal about 6 yearsWhat is generateBoundary?
-
Debaprio B about 6 yearsDon't you need to set the 'Content-Length' in the headers?
-
Mannam Brahmam about 6 yearsYou can add dear,,. body.setValue("(myRequestData.length)", forHTTPHeaderField: "Content-Length")
-
Debaprio B about 6 yearsYeah. We must add the “Content-Length”, otherwise multipart doesn’t work 🙂
-
aidinMC over 5 years
-
emmics over 4 yearsFor future cases: This is done so much easier with frameworks like Moya.
-
Edward Brey almost 4 yearsThe final
\r\n
after the close-delimiter (\r\n--\(boundary)--
) is optional. -
Lakshmi Yadav over 3 years@Anthony, I am able to send file to server. Now I want to send some description too with the file. Any suggestions?
-
Lakshmi Yadav over 3 years@jansma, Can we add URL as a file name?
-
Lakshmi Yadav over 3 years@jansma, I want to send one url with the file. How can I send?
-
Rohit about 3 yearsHow to add extra json input along with this?
-
Nazmul Hasan about 3 yearsI change it from data.append("Content-Type: image/png\r\n\r\n".data(using: .utf8)!) to data.append("Content-Type: \"content-type header\"\r\n\r\n".data(using: .utf8)!) for my end to workable this coe
-
Abrcd18 over 2 yearsHi! Could you please say paramName is base64String?
-
Farras Doko over 2 yearsso
filename
is required, or the upload will be ignored. The filename can be filled with whatever (not the actual filename).