Related
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 ?
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/
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
}
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
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()
}
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.
extension URLRequest {
mutating func setMultipartFormDataBody(params: [String: (Data, filename: String?)]) {
let boundary = UUID().uuidString
self.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var body = Data()
for (key, (data, filename)) in params {
body.append("--\(boundary)\r\n")
if let filename = filename {
body.append("Content-Disposition: form-data; name=\"\(key)\"; filename=\"\(filename)\"\r\n")
}
else {
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n")
}
body.append("\r\n")
body.append(data)
body.append("\r\n")
}
body.append("--\(boundary)--")
self.httpBody = body
}
}
extension Data {
mutating func append(_ s: String) {
self.append(s.data(using: .utf8)!)
}
}
The first thing I noticed is the application/octet-stream as Conten-Type, this is usually used when the file type is unknown. Some web frameworks/libraries will reject this content-type if an image is required.
Second, I can't see the post length anywhere, try to add it:
body.appendString("--\(boundary)--\r\n")
// set the content-length
request.setValue("\(body.length)", forHTTPHeaderField:"Content-Length")
class func postMultiPartdata( postdatadictionary: [AnyHashable: Any], apikey: String, completion: #escaping (Any) -> () ) {
if Utils().isConnectedToNetwork() == false
{
Utils().showMessage("Check internet")
return
}
let strURL = "http://redspark.biz/dropp/api/\(apikey)"
let url = URL(string: strURL)
var urlRequest = URLRequest(url: url!)
urlRequest.httpMethod = "POST"
let body = NSMutableData();
let boundary = "---------------------------14737809831466499882746641449"
let contentType = "multipart/form-data; boundary=\(boundary)"
urlRequest.addValue(contentType, forHTTPHeaderField: "Content-Type")
for (key, value) in postdatadictionary {
if(value is Data)
{
let TimeStamp = "\(Date().timeIntervalSince1970 * 1000)"
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"\(key)\"; filename=\"\(TimeStamp)\"\r\n".data(using:.utf8)!)
body.append("field_mobileinfo_image\r\n".data(using: .utf8)!)
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"files[field_mobileinfo_image]\"; filename=\"img.jpg\"\r\n".data(using: .utf8)!)
body.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
// var imgData: Data? = nil
// if let aKey = value as? Data {
// imgData = NSData(data: aKey) as Data
// }
body.append(value as! Data)
}
else
{
if let anEncoding = "--\(boundary)\r\n".data(using: .utf8) {
body.append(anEncoding)
}
if let anEncoding = "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8) {
body.append(anEncoding)
}
if let aKey = postdatadictionary[key], let anEncoding = "\(aKey)".data(using: .utf8) {
body.append(anEncoding)
}
if let anEncoding = "\r\n".data(using: .utf8) {
body.append(anEncoding)
}
}
}
if let anEncoding = "--\(boundary)--\r\n".data(using: .utf8) {
body.append(anEncoding)
}
// setting the body of the post to the reqeust
urlRequest.httpBody = body as Data
URLSession.shared.dataTask(with:urlRequest) { (data, response, error) in
if error != nil {
print(error!)
completion("")
} else {
var dictonary:NSDictionary?
do {
dictonary = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
if let myDictionary = dictonary
{
completion(myDictionary)
}
} catch let error as NSError {
completion(error)
}
}
Utils().HideLoader()
}.resume()
}
following those answers here is my implementation with a concrete example for video and audio. notice that the boundary between elements has such form --boundary while the last boundary is written --boudary--.
let url = URL(string: "https://...")!
let boundary = UUID().uuidString
var request = URLRequest(url: url)
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.httpMethod = "post"
var data = Data()
data.addMultiPart(boundary: boundary, name: "metadata", filename: "metadata.json", contentType: "application/json", data: metaData)
let ext = fileUrl.pathExtension.lowercased()
let isImage = ["jpg","jpeg","png"].contains(ext)
let contentType = isImage ? "image/\(ext)" : "video/\(ext)"
let mediaData = try! Data(contentsOf: fileUrl)
data.addMultiPart(boundary: boundary, name: "file", filename: fileUrl.lastPathComponent, contentType: contentType, data: mediaData)
data.addMultiPartEnd(boundary: boundary)
request.httpBody = data
let task = session.dataTask(with: request)
task.resume()
private extension Data {
mutating func addMultiPart(boundary: String, name: String, filename: String, contentType: String, data: Data) {
print("adding boundary: \(boundary), name: \(name), filename: \(filename), contentType: \(contentType) data length: \(data.count) ")
self.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
self.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(filename)\"\r\n".data(using: .utf8)!)
self.append("Content-Type: \(contentType)\r\n\r\n".data(using: .utf8)!)
self.append(data)
}
mutating func addMultiPartEnd(boundary: String) {
self.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
}
}
I implemented Upload image using Multi-part in Swift 4:
Here is the code. Please have a look
//MARK: Uplaod User Profile Pic
func uploadImageToServerFromApp(nameOfApi : NSString, parameters : NSString, uploadedImage : UIImage, withCurrentTask :RequestType, andDelegate :AnyObject)->Void {
if self.isConnectedToNetwork(){
currentTask = withCurrentTask
let myRequestUrl = NSString(format: "%#%#%#",GlobalConstants.KBaseURL,nameOfApi,parameters)
let url = (myRequestUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed))!
var request : NSMutableURLRequest = NSMutableURLRequest()
request = URLRequest(url: URL(string:url as String)!) as! NSMutableURLRequest
request.httpMethod = "POST"
let boundary = generateBoundaryString()
//define the multipart request type
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let image_data = UIImagePNGRepresentation(uploadedImage)
if(image_data == nil){
return
}
let body = NSMutableData()
let fname = "image.png"
let mimetype = "image/png"
//define the data post parameter
body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Disposition:form-data; name=\"image\"\r\n\r\n".data(using: String.Encoding.utf8)!)
body.append("hi\r\n".data(using: String.Encoding.utf8)!)
body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Disposition:form-data; name=\"image\"; filename=\"\(fname)\"\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Type: \(mimetype)\r\n\r\n".data(using: String.Encoding.utf8)!)
body.append(image_data!)
body.append("\r\n".data(using: String.Encoding.utf8)!)
body.append("--\(boundary)--\r\n".data(using: String.Encoding.utf8)!)
request.httpBody = body as Data
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
guard let data = data, error == nil else { // check for fundamental networking error
// print("error=\(String(describing: error))")
self.showAlertMessage(title: "App name", message: "Server not responding, please try later")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
// print("statusCode should be 200, but is \(httpStatus.statusCode)")
// print("response = \(String(describing: response))")
self.delegate?.internetConnectionFailedIssue()
}else{
do {
self.responseDictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! NSDictionary
// self.Responsedata = data as NSData
//self.responseDictionary = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [String: AnyObject] as NSDictionary;
self.delegate?.responseReceived()
} catch {
//print("error serializing JSON: \(error)")
}
}
}
task.resume()
}
else{
// print("Internet Connection not Available!")
self.showAlertMessage(title: "App Name", message: "No Internet Connection..")
}
}
func generateBoundaryString() -> String
{
return "Boundary-\(NSUUID().uuidString)"
}
Very good video and code:
https://www.youtube.com/watch?v=8GH0yMPvQFU
https://github.com/Kilo-Loco/URLSessionMPFD/blob/master/URLSessionMPFD/ViewController.swift
import UIKit
typealias Parameters = [String: String]
class ViewController: UIViewController {
#IBAction func getRequest(_ sender: Any) {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { return }
var request = URLRequest(url: url)
let boundary = generateBoundary()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let dataBody = createDataBody(withParameters: nil, media: nil, 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()
}
#IBAction func postRequest(_ sender: Any) {
let parameters = ["name": "MyTestFile123321",
"description": "My tutorial test file for MPFD uploads"]
guard let mediaImage = Media(withImage: #imageLiteral(resourceName: "testImage"), forKey: "image") else { return }
guard let url = URL(string: "https://api.imgur.com/3/image") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
let boundary = generateBoundary()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.addValue("Client-ID f65203f7020dddc", forHTTPHeaderField: "Authorization")
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: Parameters?, 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/jpeg"
self.filename = "kyleleeheadiconimage234567.jpg"
guard let data = UIImageJPEGRepresentation(image, 0.7) else { return nil }
self.data = 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/jpeg"
self.filename = "kyleleeheadiconimage234567.jpg"
guard let data = UIImageJPEGRepresentation(image, 0.7) else { return nil }
self.data = data
}
}
func getHeaders(inAuthToken: String = "") -> HTTPHeaders {
var header : HTTPHeaders = [:]
header["Authorization"] = "Bearer \(self.token)"
print(header)
return header
}
func putRequestWithMultipart(url: String, parameters:[String: Any], completion: #escaping(Bool) -> Void) {
let url = "http://3.6.147.149:3000/api/v1/\(url)"
//
for (key, value) in parameters {
if let temp = value as? String {
multipartFormData.append(temp.data(using: .utf8)!, withName: key )
}
if let temp = value as? Int {
multipartFormData.append("\(temp)".data(using: .utf8)!, withName: key )
}
if let temp = value as? NSArray {
temp.forEach({ element in
let keyObj = key + "[]"
if let string = element as? String {
multipartFormData.append(string.data(using: .utf8)!, withName: keyObj)
} else
if let num = element as? Int {
let value = "\(num)"
multipartFormData.append(value.data(using: .utf8)!, withName: keyObj)
}
})
}
if let data = value as? Data {
if key == "image" {
multipartFormData.append(data, withName: "image", fileName: "\(Date.init().timeIntervalSince1970).png", mimeType: "image/png")
}
}
}
},
usingThreshold: UInt64.init(),
to: url,
method: .put,
headers: headers
) { (result) in
switch result {
case .success(let upload, _, _):
upload.uploadProgress(closure: { (progress) in
print("Upload Progress: \(progress.fractionCompleted)")
})
upload.responseJSON { response in
print(response)
if let data = response.data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any] ?? [:]
print(json)
} catch {
print("Something went wrong")
}
completion(true)
}
}
case .failure(let encodingError):
print(encodingError)
completion(false)
}
}
}
}
I want to post image with parameters to server with url session, Here what I try -
var groupImg: UIImage?
var grupId: String = ""
var grupName: String = ""
var creator: String = "xahiw"
var lati = "30.6425°N"
var long = "76.8173°E"
and here's the code on the action of button
#IBAction func onClickSubmitBtn(_ sender: Any) {
let url = URL(string: "http://example/api/create")!
var request = URLRequest(url: url)
request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let parameters: [String: Any] = [
"name": grupName,
"category": grupId,
"picture": groupImg!,
"createdBy": creator,
"lat": lati,
"lng": long
]
request.httpBody = parameters.percentEncoded()
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data,
let response = response as? HTTPURLResponse,
error == nil else {
print("error", error ?? "Unknown error")
return
}
guard (200 ... 299) ~= response.statusCode else {
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(String(describing: responseString))")
}
task.resume()
}
extension Dictionary {
func percentEncoded() -> Data? {
return map { key, value in
let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
return escapedKey + "=" + escapedValue
}
.joined(separator: "&")
.data(using: .utf8)
}
}
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]#"
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return allowed
}()
}
but i unable to post data i get the status code -- 400 when i click on button, can anyone tell me how to do this, is it done with different way (upload image with parameters )
You can use a simple function to create a body for your request.
func createBodyWithParameters(parameters: [String: String], filePathKey: String?, imageDataKey: Data?, boundary: String) -> Data {
var body = Data();
for (key, value) in parameters {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
let filename = "image_name.jpg"
let mimetype = "image/jpg"
body.appendString("--\(boundary)\r\n")
if let filePath = filePathKey, let imageData = imageDataKey {
body.appendString("Content-Disposition: form-data; name=\"\(filePath)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimetype)\r\n\r\n")
body.append(imageData)
body.appendString("\r\n")
body.appendString("--\(boundary)--\r\n")
}
return body
}
and assign it to the request body.
request.httpBody = createBodyWithParameters(parameters: ["key":"Value"], filePathKey: "file", imageDataKey: imageData, boundary: "Boundary-\(NSUUID().uuidString)");
For appending string to the data, you can use an extension like this.
extension Data {
mutating func appendString(_ string: String) {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
append(data!)
}
}
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) {
var stringUrl = “abc.com”
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
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var data = Data()
if parameters != nil{
for(key, value) in parameters!{
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() {
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)
}
data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
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 {
mutating func append(_ string: String, using encoding: String.Encoding = .utf8) {
if let data = string.data(using: encoding) {
append(data)
}
}
}
I am facing some problem in a POST query; please help. It always sends me a 400 Bad request response. I have checked everything thoroughly but its just not working. Please help.
Code is -
struct RegisterService {
var authToken: String = ""
var baseURL = NSURL(string: "")
func getToken(name: String, email: String, password: String, cfmpassword: String, completion: (AuthToken? -> Void)) {
let request = NSMutableURLRequest(URL: baseURL!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
let params = ["accept":"on", "cfmpassword":"\(cfmpassword)",
"email":"\(email)", "name":"\(name)", "password":"\(password)"]
as Dictionary<String, AnyObject>
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
} catch {
print("Request Unsuccessful")
}
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
print(request)
print(data)
print(response)
if let httpResponse = response as? NSHTTPURLResponse {
switch httpResponse.statusCode {
case 200:
// 2. Create JSON object with data
do {
let jsonDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String: AnyObject]
let authToken = AuthToken(tokenDictionary: jsonDictionary!)
completion(authToken)
} catch {
print("Fetch failed: \((error as NSError).localizedDescription)")
}
default:
print("GET request not successful. HTTP status code: \(httpResponse.statusCode)")
NSOperationQueue.mainQueue().addOperationWithBlock {
completion(nil)
}
}
} else {
print("Error: Not a valid HTTP response")
NSOperationQueue.mainQueue().addOperationWithBlock {
completion(nil)
}
}
})
task.resume()
}
}
let params = ["accept":"on", "cfmpassword":"\(cfmpassword)", "email":"\(email)", "name":"\(name)", "password":"\(password)"] as Dictionary<String, AnyObject>
let boundary:String = "---------------------------14737809831466499882746641449"
let body:NSMutableData = NSMutableData()
if params != nil
{
for (key, value) in params!
{
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
}
body.appendString("--\(boundary)--\r\n")
request.setValue("\(body.length)", forHTTPHeaderField:"Content-Length")
request.HTTPBody = body
var urlString = ""
let url = NSURL(string: urlString)
let theRequest = NSMutableURLRequest(URL: url!)
theRequest.HTTPMethod = "POST"
let parameters = []
var err:NSError!
do{
theRequest.HTTPBody = try NSJSONSerialization.dataWithJSONObject(parameters, options: [])
}
catch let error as NSError{
err = error
theRequest.HTTPBody = nil
}
theRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
theRequest.addValue("application/json", forHTTPHeaderField: "Accept")
Here I am using a 3rd party class library for a signature . How can I POST it to webservices in the form an image?
Simple example for you:
//create session
var urlSession : NSURLSession!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
urlSession = NSURLSession(configuration: configuration)
func updateUserDetails(image : UIImage? , dictUserDetails: [String: String?] ,completionClosure: (repos :NSDictionary) ->()) {
let request = NSMutableURLRequest(URL: NSURL(string:"Your URL" )!)
request.HTTPMethod = "POST"
let boundary = generateBoundaryString()
let token = getUserToken() as String
request.addValue("Token " + token, forHTTPHeaderField: "Authorization") // add token if you need
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
guard image != nil else {
request.HTTPBody = createBodyWithParameters(dictUserDetails, filePathKey: "profile_pic", imageDataKey: nil, boundary: boundary)
let postDataTask = self.urlSession.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if (error != nil) {
print(error!.localizedDescription)
}
if (data != nil) {
if let jsonResult: NSDictionary = (try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as? NSDictionary
{
completionClosure(repos: jsonResult)
}
else
{
completionClosure(repos: NSDictionary())
}
} else {
completionClosure(repos: NSDictionary())
}
})
postDataTask.resume()
return
}
let imageData = UIImageJPEGRepresentation(image!, 0.3)
request.HTTPBody = createBodyWithParameters(dictUserDetails, filePathKey: "profile_pic", imageDataKey: imageData, boundary: boundary)
let postDataTask = self.urlSession.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if (error != nil) {
print(error!.localizedDescription)
}
if (data != nil) {
if let jsonResult: NSDictionary = (try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as? NSDictionary
{
completionClosure(repos: jsonResult)
}
else
{
completionClosure(repos: NSDictionary())
}
} else {
completionClosure(repos: NSDictionary())
}
})
postDataTask.resume()
}
Helper Functions:
//Generate boundary
func generateBoundaryString() -> String {
return "Boundary-\(NSUUID().UUIDString)"
}
//create body
func createBodyWithParameters(parameters: [String: String?]?, filePathKey: String?, imageDataKey: NSData?, boundary: String) -> NSData {
let body = NSMutableData()
if parameters != nil {
for (key, value) in parameters! {
if value != nil {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value!)\r\n")
}
}
}
if imageDataKey != nil
{
let filename = "user-profile.jpg"
let mimetype = "image/jpg"
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimetype)\r\n\r\n")
body.appendData(imageDataKey!)
body.appendString("\r\n")
body.appendString("--\(boundary)--\r\n")
}
return body
}
Now you can use it this way:
updateUserDetails(yourImage, dictUserDetails: [
//parameters example
"date_of_birth": birthDate,
"first_name": firstName,
"last_name" : lastName,
"email" : email,
"phone_number" : phoneNumber,
"address" : address,
"martial_status" : userMaritialStatus
], completionClosure: { (repos) -> () in
print(repos)
})
Hope it will help.
You did not say which third party library you are using, so you can use Alamofire for it, using this code:
Alamofire.upload(
.POST,
"your_API_URL_here",
multipartFormData: { multipartFormData in
if let _ = image {
if let imageData = UIImageJPEGRepresentation(image!, 1.0) {
multipartFormData.appendBodyPart(data: imageData, name: "file", fileName: "file.jpeg", mimeType: "image/jpeg")
} else {
print(image)
}
}
}, encodingCompletion: {
encodingResult in
switch encodingResult {
case .Success(let upload, _, _):
upload.responseJSON { response in
switch response.result {
case .Success:
print("Success")
case .Failure(let error):
print(error)
}
}
case .Failure(let encodingError):
print(encodingError)
}
}
)
}
} catch _ {
}
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 ?
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/
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
}
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
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()
}
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.
extension URLRequest {
mutating func setMultipartFormDataBody(params: [String: (Data, filename: String?)]) {
let boundary = UUID().uuidString
self.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var body = Data()
for (key, (data, filename)) in params {
body.append("--\(boundary)\r\n")
if let filename = filename {
body.append("Content-Disposition: form-data; name=\"\(key)\"; filename=\"\(filename)\"\r\n")
}
else {
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n")
}
body.append("\r\n")
body.append(data)
body.append("\r\n")
}
body.append("--\(boundary)--")
self.httpBody = body
}
}
extension Data {
mutating func append(_ s: String) {
self.append(s.data(using: .utf8)!)
}
}
The first thing I noticed is the application/octet-stream as Conten-Type, this is usually used when the file type is unknown. Some web frameworks/libraries will reject this content-type if an image is required.
Second, I can't see the post length anywhere, try to add it:
body.appendString("--\(boundary)--\r\n")
// set the content-length
request.setValue("\(body.length)", forHTTPHeaderField:"Content-Length")
class func postMultiPartdata( postdatadictionary: [AnyHashable: Any], apikey: String, completion: #escaping (Any) -> () ) {
if Utils().isConnectedToNetwork() == false
{
Utils().showMessage("Check internet")
return
}
let strURL = "http://redspark.biz/dropp/api/\(apikey)"
let url = URL(string: strURL)
var urlRequest = URLRequest(url: url!)
urlRequest.httpMethod = "POST"
let body = NSMutableData();
let boundary = "---------------------------14737809831466499882746641449"
let contentType = "multipart/form-data; boundary=\(boundary)"
urlRequest.addValue(contentType, forHTTPHeaderField: "Content-Type")
for (key, value) in postdatadictionary {
if(value is Data)
{
let TimeStamp = "\(Date().timeIntervalSince1970 * 1000)"
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"\(key)\"; filename=\"\(TimeStamp)\"\r\n".data(using:.utf8)!)
body.append("field_mobileinfo_image\r\n".data(using: .utf8)!)
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"files[field_mobileinfo_image]\"; filename=\"img.jpg\"\r\n".data(using: .utf8)!)
body.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
// var imgData: Data? = nil
// if let aKey = value as? Data {
// imgData = NSData(data: aKey) as Data
// }
body.append(value as! Data)
}
else
{
if let anEncoding = "--\(boundary)\r\n".data(using: .utf8) {
body.append(anEncoding)
}
if let anEncoding = "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8) {
body.append(anEncoding)
}
if let aKey = postdatadictionary[key], let anEncoding = "\(aKey)".data(using: .utf8) {
body.append(anEncoding)
}
if let anEncoding = "\r\n".data(using: .utf8) {
body.append(anEncoding)
}
}
}
if let anEncoding = "--\(boundary)--\r\n".data(using: .utf8) {
body.append(anEncoding)
}
// setting the body of the post to the reqeust
urlRequest.httpBody = body as Data
URLSession.shared.dataTask(with:urlRequest) { (data, response, error) in
if error != nil {
print(error!)
completion("")
} else {
var dictonary:NSDictionary?
do {
dictonary = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
if let myDictionary = dictonary
{
completion(myDictionary)
}
} catch let error as NSError {
completion(error)
}
}
Utils().HideLoader()
}.resume()
}
following those answers here is my implementation with a concrete example for video and audio. notice that the boundary between elements has such form --boundary while the last boundary is written --boudary--.
let url = URL(string: "https://...")!
let boundary = UUID().uuidString
var request = URLRequest(url: url)
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.httpMethod = "post"
var data = Data()
data.addMultiPart(boundary: boundary, name: "metadata", filename: "metadata.json", contentType: "application/json", data: metaData)
let ext = fileUrl.pathExtension.lowercased()
let isImage = ["jpg","jpeg","png"].contains(ext)
let contentType = isImage ? "image/\(ext)" : "video/\(ext)"
let mediaData = try! Data(contentsOf: fileUrl)
data.addMultiPart(boundary: boundary, name: "file", filename: fileUrl.lastPathComponent, contentType: contentType, data: mediaData)
data.addMultiPartEnd(boundary: boundary)
request.httpBody = data
let task = session.dataTask(with: request)
task.resume()
private extension Data {
mutating func addMultiPart(boundary: String, name: String, filename: String, contentType: String, data: Data) {
print("adding boundary: \(boundary), name: \(name), filename: \(filename), contentType: \(contentType) data length: \(data.count) ")
self.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
self.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(filename)\"\r\n".data(using: .utf8)!)
self.append("Content-Type: \(contentType)\r\n\r\n".data(using: .utf8)!)
self.append(data)
}
mutating func addMultiPartEnd(boundary: String) {
self.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
}
}
I implemented Upload image using Multi-part in Swift 4:
Here is the code. Please have a look
//MARK: Uplaod User Profile Pic
func uploadImageToServerFromApp(nameOfApi : NSString, parameters : NSString, uploadedImage : UIImage, withCurrentTask :RequestType, andDelegate :AnyObject)->Void {
if self.isConnectedToNetwork(){
currentTask = withCurrentTask
let myRequestUrl = NSString(format: "%#%#%#",GlobalConstants.KBaseURL,nameOfApi,parameters)
let url = (myRequestUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed))!
var request : NSMutableURLRequest = NSMutableURLRequest()
request = URLRequest(url: URL(string:url as String)!) as! NSMutableURLRequest
request.httpMethod = "POST"
let boundary = generateBoundaryString()
//define the multipart request type
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let image_data = UIImagePNGRepresentation(uploadedImage)
if(image_data == nil){
return
}
let body = NSMutableData()
let fname = "image.png"
let mimetype = "image/png"
//define the data post parameter
body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Disposition:form-data; name=\"image\"\r\n\r\n".data(using: String.Encoding.utf8)!)
body.append("hi\r\n".data(using: String.Encoding.utf8)!)
body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Disposition:form-data; name=\"image\"; filename=\"\(fname)\"\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Type: \(mimetype)\r\n\r\n".data(using: String.Encoding.utf8)!)
body.append(image_data!)
body.append("\r\n".data(using: String.Encoding.utf8)!)
body.append("--\(boundary)--\r\n".data(using: String.Encoding.utf8)!)
request.httpBody = body as Data
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
guard let data = data, error == nil else { // check for fundamental networking error
// print("error=\(String(describing: error))")
self.showAlertMessage(title: "App name", message: "Server not responding, please try later")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
// print("statusCode should be 200, but is \(httpStatus.statusCode)")
// print("response = \(String(describing: response))")
self.delegate?.internetConnectionFailedIssue()
}else{
do {
self.responseDictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! NSDictionary
// self.Responsedata = data as NSData
//self.responseDictionary = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [String: AnyObject] as NSDictionary;
self.delegate?.responseReceived()
} catch {
//print("error serializing JSON: \(error)")
}
}
}
task.resume()
}
else{
// print("Internet Connection not Available!")
self.showAlertMessage(title: "App Name", message: "No Internet Connection..")
}
}
func generateBoundaryString() -> String
{
return "Boundary-\(NSUUID().uuidString)"
}
Very good video and code:
https://www.youtube.com/watch?v=8GH0yMPvQFU
https://github.com/Kilo-Loco/URLSessionMPFD/blob/master/URLSessionMPFD/ViewController.swift
import UIKit
typealias Parameters = [String: String]
class ViewController: UIViewController {
#IBAction func getRequest(_ sender: Any) {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { return }
var request = URLRequest(url: url)
let boundary = generateBoundary()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let dataBody = createDataBody(withParameters: nil, media: nil, 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()
}
#IBAction func postRequest(_ sender: Any) {
let parameters = ["name": "MyTestFile123321",
"description": "My tutorial test file for MPFD uploads"]
guard let mediaImage = Media(withImage: #imageLiteral(resourceName: "testImage"), forKey: "image") else { return }
guard let url = URL(string: "https://api.imgur.com/3/image") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
let boundary = generateBoundary()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.addValue("Client-ID f65203f7020dddc", forHTTPHeaderField: "Authorization")
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: Parameters?, 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/jpeg"
self.filename = "kyleleeheadiconimage234567.jpg"
guard let data = UIImageJPEGRepresentation(image, 0.7) else { return nil }
self.data = 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/jpeg"
self.filename = "kyleleeheadiconimage234567.jpg"
guard let data = UIImageJPEGRepresentation(image, 0.7) else { return nil }
self.data = data
}
}
func getHeaders(inAuthToken: String = "") -> HTTPHeaders {
var header : HTTPHeaders = [:]
header["Authorization"] = "Bearer \(self.token)"
print(header)
return header
}
func putRequestWithMultipart(url: String, parameters:[String: Any], completion: #escaping(Bool) -> Void) {
let url = "http://3.6.147.149:3000/api/v1/\(url)"
//
for (key, value) in parameters {
if let temp = value as? String {
multipartFormData.append(temp.data(using: .utf8)!, withName: key )
}
if let temp = value as? Int {
multipartFormData.append("\(temp)".data(using: .utf8)!, withName: key )
}
if let temp = value as? NSArray {
temp.forEach({ element in
let keyObj = key + "[]"
if let string = element as? String {
multipartFormData.append(string.data(using: .utf8)!, withName: keyObj)
} else
if let num = element as? Int {
let value = "\(num)"
multipartFormData.append(value.data(using: .utf8)!, withName: keyObj)
}
})
}
if let data = value as? Data {
if key == "image" {
multipartFormData.append(data, withName: "image", fileName: "\(Date.init().timeIntervalSince1970).png", mimeType: "image/png")
}
}
}
},
usingThreshold: UInt64.init(),
to: url,
method: .put,
headers: headers
) { (result) in
switch result {
case .success(let upload, _, _):
upload.uploadProgress(closure: { (progress) in
print("Upload Progress: \(progress.fractionCompleted)")
})
upload.responseJSON { response in
print(response)
if let data = response.data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any] ?? [:]
print(json)
} catch {
print("Something went wrong")
}
completion(true)
}
}
case .failure(let encodingError):
print(encodingError)
completion(false)
}
}
}
}