Swift: Sending Images Object via URLRequest - ios

I have an array of an object that has the following values:
var pictures = [Pictures]
struct Pictures: Codable, Hashable {
var picName1: String
var picName2: String
var picName3: String
var picFile1: UIImage
var picFile2: UIImage
var picFile3: UIImage
}
And I want to send it to my API through URLRequest.
I am encountering some issues with HTTP structuring.
I keep getting the response: "Failed to read the request form. Multipart body length limit 16384 exceeded."
This is my network/API call code:
func sendPicturesToServer() {
let url = URL(string: "https://myapicall.com/noodles")
let body = try! JSONEncoder().encode(pictures)
let boundary = "---------------------------14737809831466499882746641449"
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.httpBody = body
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
do {
let serializedResponse = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print("Serialized Response: \(serializedResponse)")
} catch {
print(error)
}
}
task.resume()
}
}
Anyone have any suggestions?

func postApiDataWithMultipartForm<T:Decodable>(requestUrl: URL, request: ImageRequest, resultType: T.Type, completionHandler:#escaping(_ result: T)-> Void)
{
var urlRequest = URLRequest(url: requestUrl)
let lineBreak = "\r\n"
urlRequest.httpMethod = "post"
let boundary = "---------------------------------\(UUID().uuidString)"
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "content-type")
var requestData = Data()
requestData.append("--\(boundary)\r\n" .data(using: .utf8)!)
requestData.append("content-disposition: form-data; name=\"attachment\" \(lineBreak + lineBreak)" .data(using: .utf8)!)
requestData.append(request.attachment .data(using: .utf8)!)
requestData.append("\(lineBreak)--\(boundary)\r\n" .data(using: .utf8)!)
requestData.append("content-disposition: form-data; name=\"fileName\" \(lineBreak + lineBreak)" .data(using: .utf8)!)
requestData.append("\(request.fileName + lineBreak)" .data(using: .utf8)!)
requestData.append("--\(boundary)--\(lineBreak)" .data(using: .utf8)!)
urlRequest.addValue("\(requestData.count)", forHTTPHeaderField: "content-length")
urlRequest.httpBody = requestData
// let multipartStr = String(decoding: requestData, as: UTF8.self) //to view the multipart form string
URLSession.shared.dataTask(with: urlRequest) { (data, httpUrlResponse, error) in
if(error == nil && data != nil && data?.count != 0)
{
// let dataStr = String(decoding: requestData, as: UTF8.self) //to view the data you receive from the API
do {
let response = try JSONDecoder().decode(T.self, from: data!)
_=completionHandler(response)
}
catch let decodingError {
debugPrint(decodingError)
}
}
}.resume()
}

Related

How to create multipart form data request for image and json data using URLSession- Swift [duplicate]

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)
}
}
}
}

Server response throwing Unable to process while uploading document/Photo to server in Swift

I am trying to uploading document to server. In Action sheet, User can choose Photo from gallery to Document to upload.
So, I am taking either photo or document and converting it into Data.
After that I am sending that data to server along with two parameters (keys, values) with multipart.
But, Unfortunately I am getting error like Unable to process from server, But same thing working in Android domain.
Here is my code:
func uploadDocument(documentId:Int, data: Data, filePath: String, categoryType: String, completion: #escaping uploadDocumentClosure) {
let url = "https://somedomain.com/uploadDocument"
let requestURL = URL(string: url)!
let request = NSMutableURLRequest(url: requestURL)
request.cachePolicy = .reloadIgnoringLocalCacheData
request.httpShouldHandleCookies = false
request.timeoutInterval = 30
request.httpMethod = "POST"
let filename = "avatar.png"
// generate boundary string using a unique per-app string
let boundary = UUID().uuidString
let docData = data
let idStr = "id"
let docTypeStr = "documentType"
let file = "file"
var data = Data()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.addValue("Bearer \(accessToken ?? "")", forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"\(docTypeStr)\"\r\n\r\n".data(using: .utf8)!)
data.append("\(categoryType)".data(using: .utf8)!)
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"\(file)\"\r\n\r\n".data(using: .utf8)!)
// 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=\"file\"; filename=\"\(filename)\"\r\n".data(using: .utf8)!)
//
if let dataa = "Content-Disposition: form-data; name=\"\(filename)\"; filename=\"image.jpg\"\r\n".data(using: .utf8) {
data.append(dataa)
}
data.append("Content-Type: image/png\r\n\r\n".data(using: .utf8)!)
data.append(docData)
data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
self.serviceManager.async(request: request as URLRequest) { (innerClosure) in
do {
let response = try innerClosure()
guard let json = response.jsonObject else {
completion({ throw JSONErrorType.parsingError })
return
}
let jsonData = try JSONSerialization.data(withJSONObject: json, options: [])
let responseModel = try JSONDecoder().decode(EditProfilePhotoUploadResponse.self, from: jsonData)
completion({ return responseModel })
} catch {
completion({ throw error })
}
}
}
And the sever response is below
{
"status" : "E",
"message" : "Unable to process.",
"data" : null,
"messageList" : null
}
With status code 400 bad request.
In Android they simply sending below code and its working fine.
#Multipart
#POST("somedomain.com/uploadDocument")
suspend fun uploadDocument(
#Part file: MultipartBody.Part,
#Query("documentType") documentType: String
): GenericMessageResponse
Any suggestions?
After much debugging, I have fixed the issue with below solution.
I used below library
https://github.com/Fyrts/Multipart.git
Here my code changes are below
func uploadDocument(imageData: Data, filePath: String, fileName: String, categoryType: String, completion: #escaping uploadDocumentClosure) {
let url = "https://wwww.somedomain.com/uploadDocument"
let requestURL = URL(string: url)!
let imageeData = ImageData(name: fileName, filePath: filePath, imagedata: imageData)
let string = imageeData.filePath
let mimeType = string.mimeType() //here list of mimes we can get from predefined
let fileContents = try! Data(contentsOf: URL(string: filePath)!)
var message = Multipart(type: .formData)
message.append(Part.FormData(name: "documentType", value: "\(categoryType)"))
message.append(Part.FormData(name: "file", fileData: fileContents, fileName: "\(imageeData.name)", contentType: "\(mimeType)"))
var URLrequest = URLRequest(url: requestURL)
URLrequest.httpMethod = "POST"
URLrequest.setMultipartBody(message)
URLrequest.addValue("/(token)", forHTTPHeaderField: "Authorization")
self.serviceManager.async(request: URLrequest as URLRequest) { (innerClosure) in
do {
let response = try innerClosure()
guard let json = response.jsonObject else {
completion({ throw JSONErrorType.parsingError })
return
}
let jsonData = try JSONSerialization.data(withJSONObject: json, options: [])
let responseModel = try JSONDecoder().decode(EditProfilePhotoUploadResponse.self, from: jsonData)
completion({ return responseModel })
} catch {
completion({ throw error })
}
}
}

Uploading array of images using multipart/form-data in iOS swift

I am trying to upload multiple images from an array via multipart/form data using the code below.
func uploadImage<T>(resource: Resource<T>, paramName: String, fileName: [String], images: [PLMUploadImage], completionHandler: #escaping (Result<T, PLMError>) -> Void) {
let boundary = UUID().uuidString
var urlRequest = URLRequest(url: resource.url)
urlRequest.httpMethod = "POST"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
if let accessToken = resource.accessToken {
urlRequest.setValue( "\(PLMLOGINAPICONSTANT.BEARER)\(accessToken)", forHTTPHeaderField: PLMLOGINAPICONSTANT.AUTHORIZATION)
}
var data = Data()
for (index, img) in images.enumerated() {
data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
let fileName = "\(index)" + ".jpg"
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(img.image.jpeg(.medium)!)
data.append("\r\n--\(boundary)--".data(using: .utf8)!)
}
let session = URLSession.shared
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] {
PLMLogger.prettyPrint(data:json)
let result = try? JSONDecoder().decode(T.self, from: responseData!)
if let result = result {
DispatchQueue.main.async {
completionHandler(.success(result))
}
} else {
DispatchQueue.main.async {
completionHandler(.failure(.decodingError))
}
}
}
}
}).resume()
}
As you see from the above code, only the last image from the array gets uploaded.
The web API is capable of receiving multiple images.

Upload image with multipart form-data iOS in Swift

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)
}
}
}
}

session.dataTaskWithURL is not executing in proper sequence

I am calling Web service.
In that method session.dataTaskWithURL is calling after some time.I think it is not giving in proper thread?
my code below
——--------------
func callService(usr: String, pwdCode: String) ->Bool
{
var resultPage=false
let url = NSURL(string: "demourl")
var xmlParse:NSString = ""
var data : NSData!
println("Before request *****************")
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
session.dataTaskWithURL(url!,
completionHandler: {(data, response, error) in
let request = NSMutableURLRequest(URL: url!)
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
let dictionary = ["email": usr, "userPwd": pwdCode] as NSDictionary
var error: NSError?
if let body = NSJSONSerialization.dataWithJSONObject(dictionary, options: nil, error: &error) {
request.HTTPBody = body
}
else
{
println("JSON error: \(error)")
}
let xmlParse=NSString(data: data, encoding: NSUTF8StringEncoding)!
if data == nil {
println("dataTaskWithRequest error: \(error)")
return
}
let parser = NSXMLParser(data: data)
parser.delegate = self
resultPage=parser.parse()
println("******** boolVal \(resultPage)")
}).resume()
println("After request *****************")
println("resultPage Final \(resultPage)")
return resultPage;
}
Can you please help me if we can execute my method "session.dataTaskWithURL(url!,
completionHandler: {(data, response, error) in " in a proper sequence???
Thanks in advance.
var resultPage=false
let url = NSURL(string: "URL")
var xmlParse:NSString = ""
var data : NSData!
let request = NSMutableURLRequest(URL: url!)
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
let dictionary = ["email": base64String, "userPwd": base64StringPwd] as NSDictionary
var error: NSError?
if let body = NSJSONSerialization.dataWithJSONObject(dictionary, options: nil, error: &error) {
request.HTTPBody = body
} else {
println("JSON error: \(error)")
}
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
(data, response, error) in
println(NSString(data: data, encoding: NSUTF8StringEncoding))
let xmlParse=NSString(data: data, encoding: NSUTF8StringEncoding)!
if data == nil {
println("dataTaskWithRequest error: \(error)")
return
}
let parser = NSXMLParser(data: data)
println("parser \(parser)")
parser.delegate = self
resultPage=parser.parse()
if self.success==false
{
println("success \(self.success)")
self.lblMessage.hidden=false
}
}
task.resume()

Resources