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

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.

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

Swift: Sending Images Object via URLRequest

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

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

Swift - JSON data wont display in UITableview

I am trying to retrieve data from a server. I can display my data in the console.
I'm trying to display it in a UITableview but nothing happens.
I tried to create a local JSON file and I am able to display it, but when coming from the server it wont work.
let newUrl = URL(string: urlGetNotifications)
let configuration = URLSessionConfiguration.default
var session = URLSession.shared
var request = URLRequest(url: newUrl!)
session = URLSession(configuration: configuration)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.setValue(authkeyFCM, forHTTPHeaderField: "auth-key")
request.setValue(tokenFCM.string(forKey: "tokenFCM"), forHTTPHeaderField: "token")
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
DispatchQueue.main.async {
guard let j = newUrl
else{
print("data not found")
return
}
guard let d = try? Data(contentsOf: j)
else { print("failed")
return
}
guard let rootJSON = try? JSONSerialization.jsonObject(with: d, options: [])
else{ print("failedh")
return
}
if let data = data, let dataString = String(data: data, encoding: .utf8) {
if let JSON = rootJSON as? [String: Any] {
print("data: \(dataString)")
guard let jsonArray = JSON["data"] as? [[String: Any]] else {
return
}
print(jsonArray)
let name = jsonArray[0]["type"] as? String
print(name ?? "NA")
print(jsonArray.last!["created_at"] as? String as Any)
self.notificationList = jsonArray.compactMap{return NotificationData($0)}
self.tableView.reloadData()
}
}
}
})
task.resume()
create a variable for the URL and create struct contain the all param
in the main add variable of type the struck then start fetch the data
var users: [User]() = []
func fetchUsers(using url: String){
let url = URL(string: url)!
let _ = URLSession.shared.dataTask(with: url){ (data,response,error)
in
guard let data = data else {return}
do{
let userFetch = try JSONDecoder().decode([Post].self, from: data) // decode * ( Codable )
self.users = userFetch
self.load(with: userFetch)
self.userCollection = userFetch
DispatchQueue.main.async {
self.collectionView.reloadData()
}
} catch{
print("error loading data cause: \(error)")
}
}.resume()
}
I figured it out
This one works
let newUrl = URL(string: urlGetNotifications)
let configuration = URLSessionConfiguration.default
var session = URLSession.shared
var request = URLRequest(url: newUrl!)
session = URLSession(configuration: configuration)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.setValue(authkeyFCM, forHTTPHeaderField: "auth-key")
request.setValue(tokenFCM.string(forKey: "tokenFCM"), forHTTPHeaderField: "token")
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
DispatchQueue.main.async {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
//self.showSpinner(onView: self.view)
print("The Response is : ",json)
if let data = data, let dataString = String(data: data, encoding: .utf8) {
if let JSON = json as? [String: Any] {
print("dumaan ba dito")
print("data: \(dataString)")
guard let jsonArray = JSON["data"] as? [[String: Any]] else {
return
}
print(jsonArray)
let name = jsonArray[0]["type"] as? String
print(name ?? "NA")
print(jsonArray.last!["created_at"] as? String as Any)
self.notificationList = jsonArray.compactMap{return NotificationData($0)}
self.tableView.reloadData()
}
}
} catch {
print("JSON error: \(error.localizedDescription)")
}
} // end
})
task.resume()

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

Resources