Converting iPhone video to mp4 and upload to PHP Server - ios

I have an app that successfully records and uploads a file to my PHP server, unfortunately the file won't play. I received guidance that I needed to use AVAssetExportSession to convert the file to mp4 to get it to work but am having trouble incorporating this into my code correctly. I'm getting an error
Error Domain=AVFoundationErrorDomain Code=-11823 "Cannot Save"
UserInfo={NSLocalizedRecoverySuggestion=Try saving again.,
NSLocalizedDescription=Cannot Save, NSUnderlyingError=0x1d465ea50
{Error Domain=NSOSStatusErrorDomain Code=-12101 "(null)"}}
Here is my code:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print("Got a video")
if let pickedVideo:URL = (info[UIImagePickerControllerMediaURL] as? URL) {
// Save video to the main photo album
let selectorToCall = #selector(CameraVideoViewController.videoWasSavedSuccessfully(_:didFinishSavingWithError:context:))
UISaveVideoAtPathToSavedPhotosAlbum(pickedVideo.relativePath, self, selectorToCall, nil)
imageSelected = true
uuid = UUID().uuidString
if imageSelected == true {
saveFileName = "video-\(uuid).mp4"
}
// Save the video to the app directory so we can play it later
let videoData = try? Data(contentsOf: pickedVideo)
let paths = NSSearchPathForDirectoriesInDomains(
FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentsDirectory: URL = URL(fileURLWithPath: paths[0])
let dataPath = documentsDirectory.appendingPathComponent(saveFileName)
try! videoData?.write(to: dataPath, options: [])
print("Saved to " + dataPath.absoluteString)
imagePicker.dismiss(animated: true, completion: {
// Anything you want to happen when the user saves an video
self.encodeVideo(dataPath: dataPath)
self.uploadVideo(videoData!)
})
} }
// custom body of HTTP request to upload image file
func createBodyWithParams(_ parameters: [String: String]?, filePathKey: String?, videoData: Data, boundary: String) -> Data {
let body = NSMutableData();
if parameters != nil {
for (key, value) in parameters! {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
}
// if file is not selected, it will not upload a file to server, because we did not declare a name file
var filename = ""
if imageSelected == true {
filename = "video-\(uuid).mp4"
}
let mimetype = "video/mp4"
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimetype)\r\n\r\n")
body.append(videoData)
body.appendString("\r\n")
body.appendString("--\(boundary)--\r\n")
return body as Data
}
// File Conversion
func encodeVideo(dataPath: URL){
let avAsset = AVURLAsset(url: dataPath)
let startDate = Date()
let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough)
let docDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let myDocPath = NSURL(fileURLWithPath: docDir).appendingPathComponent("temp.mp4")?.absoluteString
let docDir2 = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL
let filePath = docDir2.appendingPathComponent("rendered-Video.mp4")
//uploadVideo(filePath)
//deleteFile(filePath!)
if FileManager.default.fileExists(atPath: myDocPath!){
do{
try FileManager.default.removeItem(atPath: myDocPath!)
}catch let error{
print(error)
}
}
//self.uploadVideo((myDocPath as AnyObject) as! URL)
exportSession?.outputURL = filePath
exportSession?.outputFileType = AVFileType.mp4
exportSession?.shouldOptimizeForNetworkUse = true
let start = CMTimeMakeWithSeconds(0.0, 0)
let range = CMTimeRange(start: start, duration: avAsset.duration)
exportSession?.timeRange = range
exportSession!.exportAsynchronously{() -> Void in
switch exportSession!.status{
case .failed:
print("\(exportSession!.error!)")
case .cancelled:
print("Export cancelled")
case .completed:
let endDate = Date()
let time = endDate.timeIntervalSince(startDate)
print(time)
print("Successful")
print(exportSession?.outputURL ?? "")
default:
break
}
}
}
// File Upload
func uploadVideo(_ videoData: Data) {
func createBodyWithParams(_ parameters: [String: String]?, filePathKey: String?, videoData: Data, boundary: String) -> Data {
let body = NSMutableData();
if parameters != nil {
for (key, value) in parameters! {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
}
var filename = ""
if imageSelected == true {
filename = "video-\(uuid).mp4"
}
let mimetype = "video/mp4"
body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
body.appendString(String(describing: "Content-Type: \(mimetype)\r\n\r\n".data(using: String.Encoding.utf8)))
body.append(videoData)
body.append(String(format: "\r\n").data(using: String.Encoding.utf8)!)
body.append("--\(boundary)--\r\n".data(using: String.Encoding.utf8)!)
return body as Data
}
let id = user!["id"] as! String
uuid = UUID().uuidString
let url = URL(string: "http://www.foo.com/videoposts.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let param = [
"id" : id,
"uuid" : uuid
]
// body
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
// if picture is selected, compress it by half
let imageData = Data()
// ... body
request.httpBody = createBodyWithParams(param, filePathKey: "file", videoData: imageData, boundary: boundary)
// launch session
URLSession.shared.dataTask(with: request) { data, response, error in
// get main queu to communicate back to user
DispatchQueue.main.async(execute: {
if error == nil {
do {
// json containes $returnArray from php
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
// declare new var to store json inf
guard let parseJSON = json else {
print("Error while parsing")
return
}
// get message from $returnArray["message"]
let message = parseJSON["message"]
// if there is some message - post is made
if message != nil {
// reset UI
self.postBtn.alpha = 0.4
self.imageSelected = false
// switch to another scene
self.tabBarController?.selectedIndex = 4
}
} catch {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = "\(error)"
appDelegate.infoView(message: message, color: colorSmoothRed)
})
return
}
} else {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = error!.localizedDescription
appDelegate.infoView(message: message, color: colorSmoothRed)
})
return
}
})
}.resume()
}

Try to check if there is already some file exists in the directory that you are trying to save at. You can add the following code.
do { // delete old video
try FileManager.default.removeItem(at: savePathUrl)
} catch { print(error.localizedDescription) }

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 url Session post request with parameters and image (Multipart Form-Data Requests )

I want to post image with parameters to server with url session, Here what I try -
var groupImg: UIImage?
var grupId: String = ""
var grupName: String = ""
var creator: String = "xahiw"
var lati = "30.6425°N"
var long = "76.8173°E"
and here's the code on the action of button
#IBAction func onClickSubmitBtn(_ sender: Any) {
let url = URL(string: "http://example/api/create")!
var request = URLRequest(url: url)
request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let parameters: [String: Any] = [
"name": grupName,
"category": grupId,
"picture": groupImg!,
"createdBy": creator,
"lat": lati,
"lng": long
]
request.httpBody = parameters.percentEncoded()
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data,
let response = response as? HTTPURLResponse,
error == nil else {
print("error", error ?? "Unknown error")
return
}
guard (200 ... 299) ~= response.statusCode else {
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(String(describing: responseString))")
}
task.resume()
}
extension Dictionary {
func percentEncoded() -> Data? {
return map { key, value in
let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
return escapedKey + "=" + escapedValue
}
.joined(separator: "&")
.data(using: .utf8)
}
}
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]#"
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return allowed
}()
}
but i unable to post data i get the status code -- 400 when i click on button, can anyone tell me how to do this, is it done with different way (upload image with parameters )
You can use a simple function to create a body for your request.
func createBodyWithParameters(parameters: [String: String], filePathKey: String?, imageDataKey: Data?, boundary: String) -> Data {
var body = Data();
for (key, value) in parameters {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
let filename = "image_name.jpg"
let mimetype = "image/jpg"
body.appendString("--\(boundary)\r\n")
if let filePath = filePathKey, let imageData = imageDataKey {
body.appendString("Content-Disposition: form-data; name=\"\(filePath)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimetype)\r\n\r\n")
body.append(imageData)
body.appendString("\r\n")
body.appendString("--\(boundary)--\r\n")
}
return body
}
and assign it to the request body.
request.httpBody = createBodyWithParameters(parameters: ["key":"Value"], filePathKey: "file", imageDataKey: imageData, boundary: "Boundary-\(NSUUID().uuidString)");
For appending string to the data, you can use an extension like this.
extension Data {
mutating func appendString(_ string: String) {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
append(data!)
}
}
class func request(withImages path:APIMethods, method:URLMethod, token : String?, headers:[String:String]?, parameters: [String:Any]?,imageNames : [String], images:[Data], completion: #escaping(Any?, Error?, Bool)->Void) {
var stringUrl = “abc.com”
if method == .get, let lastPath = parameters?.values.first as? String {
stringUrl += lastPath
}else{
stringUrl += token ?? ""
}
// generate boundary string using a unique per-app string
let boundary = UUID().uuidString
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
print("\n\ncomplete Url :-------------- ",stringUrl," \n\n-------------: complete Url")
guard let url = URL(string: stringUrl) else { return }
var request = URLRequest(url: url)
request.httpMethod = method.rawValue
if headers != nil{
print("\n\nHeaders :-------------- ",headers as Any,"\n\n --------------: Headers")
for (key, value) in headers! {
request.setValue(value, forHTTPHeaderField: key)
}
}
// Set Content-Type Header to multipart/form-data
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var data = Data()
if parameters != nil{
for(key, value) in parameters!{
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
data.append("\(value)".data(using: .utf8)!)
}
}
for (index,imageData) in images.enumerated() {
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"\(imageNames[index])\"; filename=\"\(imageNames[index])\"\r\n".data(using: .utf8)!)
data.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
data.append(imageData)
}
data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
session.uploadTask(with: request, from: data, completionHandler: { data, response, error in
if let checkResponse = response as? HTTPURLResponse{
if checkResponse.statusCode == 200{
guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: [JSONSerialization.ReadingOptions.allowFragments]) else {
completion(nil, error, false)
return
}
let jsonString = String(data: data, encoding: .utf8)!
print("\n\n---------------------------\n\n"+jsonString+"\n\n---------------------------\n\n")
print(json)
completion(json, nil, true)
}else{
guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) else {
completion(nil, error, false)
return
}
let jsonString = String(data: data, encoding: .utf8)!
print("\n\n---------------------------\n\n"+jsonString+"\n\n---------------------------\n\n")
print(json)
completion(json, nil, false)
}
}else{
guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) else {
completion(nil, error, false)
return
}
completion(json, nil, false)
}
}).resume()
}
extension Data {
mutating func append(_ string: String, using encoding: String.Encoding = .utf8) {
if let data = string.data(using: encoding) {
append(data)
}
}
}

Append data to post body and upload to api

I am calling one api with passing some parameter with including getting some file from icloud like pdf, doc, docx and call the api.
Now i am picking some file from icloud and i need to pass to api call. The issues is my selecting file ( pdf or doc ) is not converting to nsdata and bytes is coming as 0.So its not apending to my body parameters .Help me out where i m doing wrong
My code:
func uploadthefileToserver(){
if let url = URL(string: "https://www.exampleurl/api"){
var request = URLRequest(url: url)
let boundary:String = "Boundary-\(UUID().uuidString)"
// let request = NSMutableURLRequest(url:myUrl! as URL);
request.httpMethod = "POST"
request.timeoutInterval = 10
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let postJobData:[String:Any] = ["UserId":"107","name":"hardcodevalue"]
var dataFile: Data = Data()
print(fullDestPath) ///Users/sathish/Library/Developer/CoreSimulator/Devices/4464E7A8-0F38-4802-B645-19721D251054/data/Containers/Data/Application/714B1B8E-5872-42B9-B963-B0C51C9403D7/Documents/NewFileiCloud/iOS.DOCX"
do{
dataFile = try NSData.init(contentsOf: URL(fileURLWithPath: fullDestPath, isDirectory: true)) as Data
print(dataFile)
}catch{
print(error)
}
if(dataFile==nil) { return; }
print(dataFile) //0 bytes
request.httpBody = createBodyWithParameters(parameters: postJobData, filePathKey: "Resume", FileData: dataFile as NSData , boundary: boundary) as Data
print(postJobData)
print(dataFile)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}else if let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue){
print("****** response data = \(responseString)")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary
print(json)
let status = json!["Success"] as! String
let errMessage = json!["Message"] as? String
DispatchQueue.main.async() {
if status == "1"{
print(errMessage)
}else{
print(errMessage)
}
}
}catch{
print(error)
}
}
}; task.resume()
}
}
Not sure where i am doing wrong. I put some print functions for the referance.
Main parts :
dataFile = try NSData.init(contentsOf: URL(fileURLWithPath:
fullDestPath, isDirectory: true)) as Data
request.httpBody = createBodyWithParameters(parameters: postJobData,
filePathKey: "Resume", FileData: dataFile as NSData , boundary:
boundary) as Data
Thanks
Update :
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
print("url = \(urls)")
filePathUrl = urls
print(filePathUrl)
for urll in filePathUrl{
filepath = filePathUrl[0] as! URL
print(filepath)
filePathString = filepath.path
urlstr = NSURL(fileURLWithPath: filePathString).lastPathComponent!
print(urlstr)
// Data object to fetch weather data
do {
let weatherData = try NSData(contentsOf: filepath, options: NSData.ReadingOptions())
print(weatherData)
} catch {
print(error)
}
}
let destPath:NSArray = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
let fileManager = FileManager.default
print(destPath, "\n")
documentDir = destPath[0] as? NSString
let filePath = documentDir?.appendingPathComponent("NewFileiCloud") as! NSString
// if fileManager.fileExists(atPath: filePath as String){
do {
// try fileManager.createDirectory(atPath: filePath as String, withIntermediateDirectories: false, attributes: nil)
fullDestPath = filePath.appendingPathComponent(urlstr)
print(fullDestPath!) ///Users/sathish/Library/Developer/CoreSimulator/Devices/4464E7A8-0F38-4802-B645-19721D251054/data/Containers/Data/Application/E41634D7-681A-4C09-B3EF-5782CECCF4B0/Documents/NewFileiCloud/filke.pdf
do{
try fileManager.copyItem(atPath: filePathString!, toPath: fullDestPath)
}catch{
print("\n")
print(error)
}
}catch{
print(error)
}
// }
// ------- This is the path of the application stored filepath -------------- //
filePathLabel.text = fullDestPath
// ------------------- ---------------------------------//
// Read a file content
// fileContent = fileManager.contents(atPath: fullDestPath as String ) as! NSData
// print(fileContent)
uploadthefileToserver()
}
func createBodyWithParameters(parameters: [String: Any]?, filePathKey: String?, FileData: NSData, boundary: String) -> NSData {
let body = NSMutableData();
if parameters != nil {
for (key, value) in parameters! {
body.appendString(string:"--\(boundary)\r\n")
body.appendString(string: "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString(string: "\(value)\r\n")
}
}
return body
let filename = fullDestPath
let mimetype = "pdf/docx/text"
body.appendString(string: "--\(boundary)\r\n")
body.appendString(string: "Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
body.appendString(string: "Content-Type: \(mimetype)\r\n\r\n")
body.append(FileData as Data)
body.appendString(string: "\r\n")
body.appendString(string: "--\(boundary)--\r\n")
return body
}
You are trying to do,
Convert PDFURL to Data
Convert Data to .PDF
Storing that .PDF in Doc.Dir.
Retriving that .PDF from Doc.Dir and Pass to Server.
For the above task, you have to create folder NewFileiCloud in Doc.Dir. Then, convert pdfUrl to data and write that data to .pdf file, and then, get that .pdf path from doc.dir and convert that into Data and pass to server.
I have did sample for you. This will satisfy you.
override func viewDidAppear(_ animated: Bool) {
// I have did sample for you by taking .pdf from bundle.
if let pathPDF = Bundle.main.path(forResource: "sample", ofType: "pdf") {
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentDirectoryPath:String = path[0]
let fileManager = FileManager()
var destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appending("/NewFileiCloud"))
do {
//You have to create directory with above name.
try fileManager.createDirectory(at: destinationURLForFile, withIntermediateDirectories: true, attributes: nil)
destinationURLForFile.appendPathComponent("reader.pdf")
//YOUR PDF URL [pathPDF [my bundle path, you have to give your URL]] to DATA
let pdfData = try Data(contentsOf: URL(fileURLWithPath: pathPDF))
// WRITE ITS CONTENT to Doc.Dir.
try pdfData.write(to: destinationURLForFile, options:.atomic)
//ASSIGN PATH TO GLOBAL URL VARIABLE
fullPAth = destinationURLForFile
print("conclude ", destinationURLForFile)
uploadToServer()
}
catch(let error){
print(error)
}
}
}
func uploadToServer() {
.....
do {
// Here you can get PDF contents as Data.
// With this Data, you can pass to Server Side.
let pdfPOSTData = try Data(contentsOf: fullPAth!)
}
catch let e{
print("Catch_Not_worlk ", e)
}
......
}

Upload Pdf, Docx and image file to server using Swift 4

I am new to swift, I have been trying to upload pdf, docx and image file from local storage of a Iphone. I have written a code but it doesn't work and I keep getting Status Code: 415 from the response. Here is my code:
func uploadfileToServer(completed: #escaping () -> ()){
let theTitle = labelTitle.text
guard let url = URL(string: "http://www.--------.com/assignment/post") else {return}
var request = URLRequest.init(url: url)
request.httpMethod = "POST"
request.addValue("cf7ab8c9d4efae82b575eabd6bec76cbb86c6108391e036387f3dd5356a582171519367747000", forHTTPHeaderField: "api_key")
let boundary = generateBoundaryString()
// Set Content-Type in HTTP header.
let boundaryConstant = boundary // This should be auto-generated.
let contentType = "multipart/form-data; boundary=" + boundaryConstant
let directory = NSTemporaryDirectory()
let fileName = NSUUID().uuidString
// This returns a URL? even though it is an NSURL class method
let fullURL = NSURL.fileURL(withPathComponents: [directory, fileName])
let fileNamee = fullURL?.path
let mimeType = "text/csv"
let fieldName = "uploadFile"
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
var dataString = "--\(boundaryConstant)\r\n"
dataString += "\r\n"
dataString += "--\(boundaryConstant)--\r\n"
var theBody = Data()
let sectionID : String?
sectionID = nil
let str = "user_id=\(savedsesuid!)" + "&school_id=" + SCHOOL_ID + "&class_id=" + classID + "&section_id=\(sectionID)" + "&subject_id=\(id)"
if let b = str.data(using: .utf8) {
theBody.append(b)
}
let str1 = "&atitle=" + theTitle! + "&class_id=" + classID
if let c = str1.data(using: .utf8){
theBody.append(c)
}
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
var filePath = documentDirectory.appendingFormat("/")
filePath = filePath.appendingFormat("/Users/prashanna/Desktop/ebusiness_topic_1_handout.pdf")
let pdfData = NSData(contentsOfFile: filePath)
let file = "&afile=" + "\(pdfData)"
if let d = file.data(using: .utf8){
theBody.append(d)
}
print(theBody)
request.httpBody = theBody
URLSession.shared.dataTask(with: request) { (data, response, error) in
print(response)
if let httpResponse = response as? HTTPURLResponse {
let statuscode = httpResponse.statusCode
if statuscode == 401{
self.displayMessage(userMessage: "Sending Failed")
}else if statuscode == 200{
if error == nil{
do{
self.displayMessage(userMessage: "File Successfully Uploaded!")
DispatchQueue.main.async {
completed()
}
}
}
}
}
}.resume()
}
func generateBoundaryString() -> String {
return "Boundary-\(NSUUID().uuidString)"
}
Some solutions tell me to convert the file into Data and then send it to server while some say to directly add the file path to your body.
Need Help!
One fundamental mistake is that you are using dataTask instead of an uploadTask on your URLSession instance, e.g. uploadTask(with:from:completionHandler:)
Construction of Body Data
Here's a generic example from my own code (as requested in the comments below) of how body data might be constructed:
// imagesURLS is an optional array of URLs, i.e. imageURLS:[URL]?
if let imgURLs = imagesURLS {
for f in imgURLs {
let filename = f.lastPathComponent
let splitName = filename.split(separator: ".")
let name = String(describing: splitName.first)
let filetype = String(describing: splitName.last)
let imgBoundary = "\r\n--\(boundary)\r\nContent-Type: image/\(filetype)\r\nContent-Disposition: form-data; filename=\(filename); name=\(name)\r\n\r\n"
if let d = imgBoundary.data(using: .utf8) {
bodyData.append(d)
}
do {
let imgData = try Data(contentsOf:f, options:[])
bodyData.append(imgData)
}
catch {
// can't load image data
}
}
}
let closingBoundary = "\r\n--\(boundary)--"
if let d = closingBoundary.data(using: .utf8) {
bodyData.append(d)
}
The loop means that every item of data (in this case an image) is preceded by a boundary string and after the very last item of data the closing boundary string is added (i.e. the one ending in a double hyphen).
This works for me, in Swift4:
func uploadFiles(_ urlPath: [URL]){
if let url = URL(string: "YourURL"){
var request = URLRequest(url: url)
let boundary:String = "Boundary-\(UUID().uuidString)"
request.httpMethod = "POST"
request.timeoutInterval = 10
request.allHTTPHeaderFields = ["Content-Type": "multipart/form-data; boundary=----\(boundary)"]
for path in urlPath{
do{
var data2: Data = Data()
var data: Data = Data()
data2 = try NSData.init(contentsOf: URL.init(fileURLWithPath: path.absoluteString, isDirectory: true)) as Data
/* Use this if you have to send a JSON too.
let dic:[String:Any] = [
"Key":Value,
"Key":Value
]
for (key,value) in dic{
data.append("------\(boundary)\r\n")
data.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
data.append("\(value)\r\n")
}
*/
data.append("------\(boundary)\r\n")
//Here you have to change the Content-Type
data.append("Content-Disposition: form-data; name=\"file\"; filename=\"YourFileName\"\r\n")
data.append("Content-Type: application/YourType\r\n\r\n")
data.append(data2)
data.append("\r\n")
data.append("------\(boundary)--")
request.httpBody = data
}catch let e{
//Your errors
}
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).sync {
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { (dataS, aResponse, error) in
if let erros = error{
//Your errors
}else{
do{
let responseObj = try JSONSerialization.jsonObject(with: dataS!, options: JSONSerialization.ReadingOptions(rawValue:0)) as! [String:Any]
}catch let e{
}
}
}).resume()
}
}
}
}
extension Data{
mutating func append(_ string: String, using encoding: String.Encoding = .utf8) {
if let data = string.data(using: encoding) {
append(data)
}
}
}
Sample Code for uploading image is:
func uploadImage(){
var imageToUpload:UIImage = UIImage()
let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
if let dirPath = paths.first
{
let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent("Image2.png") //Your image name here
let image = UIImage(contentsOfFile: imageURL.path)
imageToUpload = image!
}
Alamofire.upload(multipartFormData: { (multipartFormData) in
multipartFormData.append(UIImageJPEGRepresentation(imageToUpload, 1)!, withName: "Prescription", fileName: "Profile_Image.jpeg", mimeType: "image/jpeg")
}, to:"you_URL_here")
{ (result) in
switch result {
case .success(let upload, _, _):
print(result)
upload.uploadProgress(closure: { (progress) in
print(progress)
})
upload.responseJSON { response in
//print response.result
print(response);
}
case .failure(let encodingError):
print(encodingError);
}
}
}

Upload image to server Swift 3

I'm trying to upload an image to a server in iOS using Swift 3, I've tried with Alamofire but it's not working, so I just searched for another solution here in this forum but without luck.
I found some answers that said that the problem could have been server side, but, on Android the image is uploading correctly.
This is my upload function in swift 3:
func uploadImage(image: UIImage){
let imageData = UIImageJPEGRepresentation(image, 0.1)!
let session = URLSession(configuration: URLSessionConfiguration.default)
guard let url = URL(string: uploadPicUrl) /* your API url */) else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
let boundary = "---------------------------14737809831466499882746641449"
let contentType = "multipart/form-data; boundary=\(boundary)"
request.addValue(contentType, forHTTPHeaderField: "Content-Type")
var body = Data()
body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Disposition: form-data; name=\"userfile\"; filename=\"img.jpg\"\r\n".data(using: String.Encoding.utf8)!)
body.append("Content-Transfer-Encoding: binary\r\n\r\n".data(using: String.Encoding.utf8)!)
body.append(imageData)
body.append("\r\n".data(using: String.Encoding.utf8)!)
body.append("--\(boundary)--\r\n".data(using: String.Encoding.utf8)!)
request.httpBody = body
print("request", request.debugDescription)
print("body", body.debugDescription)
let dataTask = session.dataTask(with: request) { (data, response, error) in
if let error = error {
print("Something went wrong: \(error)")
}
if let response = response {
print("Response: \n \(response)")
}
}
dataTask.resume()
}
Without using Alamofire, you can do the following:
func uploadImage(chosenimage: UIImage) {
let url = ApiList.base_url + ApiList.uploadFile_Url
let myUrl = NSURL(string: url)
let image_data = UIImagePNGRepresentation(chosenimage)
let tempData = NSMutableData()
let request = NSMutableURLRequest(url:myUrl! as URL)
request.httpMethod = "POST"
let boundary = NSUUID().uuidString
request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField:"Content-Type")
let mimetype = "image/png"
let fname = "test.png"
self.body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
self.body.append("Content-Disposition:form-data; name=\"profileUrl\"; filename=\"\(fname)\"\r\n".data(using: String.Encoding.utf8)!)
self.body.append("Content-Type: \(mimetype)\r\n\r\n".data(using: String.Encoding.utf8)!)
self.body.append(image_data!)
self.body.append("\r\n".data(using: String.Encoding.utf8)!)
let accessToken = UserDefaults.standard.value(forKey: "accessToken") as? String ?? ""
let deviceToken = UserDefaults.standard.value(forKey: "deviceToken") as? String ?? singletonclass.instance.getDeviceToken
let param = [
"accessToken":accessToken,
"deviceId":deviceToken,
"deviceType":"2"
]
for (key,value) in param {
tempData.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
tempData.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: String.Encoding.utf8)!)
tempData.append("\(value)\r\n".data(using: String.Encoding.utf8)!)
}
self.body.append(tempData as Data)
self.body.append("--\(boundary)--\r\n".data(using: String.Encoding.utf8)!)
request.httpBody = self.body as Data
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest) {
(
data, response, error) in
guard let _:NSData = data! as NSData, let _:URLResponse = response, error == nil else { return }
do
{
let responseDict = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.allowFragments) as! [String:Any]
//print("\n tyiukmqw",responseDict)
let code = responseDict.value(forKey: "code") as? String
let message = responseDict.value(forKey: "Message") as? String
singletonclass.instance.isrofilePicChagedOrNot = false
if code == "200"
{
print("success code")
DispatchQueue.main.async(execute: {
self.userProfile.image = chosenimage
UserDefaults.standard.setValue(UIImagePNGRepresentation(chosenimage), forKey: "UserProfilePicture")
singletonclass.instance.userProPic = chosenimage
})
}
else
{
DispatchQueue.main.async(execute: {
singletonclass.instance.showAlert(message!)
self.isActivityIndicatorNeed(false)
})
}
}
catch
{
}
}
task.resume()
}
Note : This function is to upload multiple files of different type using Alamofire
// upload file to server
func uploadFiles(files: [Data],completion : #escaping uploadHandler) {
let header : HTTPHeaders = ["Content-Type" : "application/x-www-form-urlencoded"] // if there's Authorization, you may add it in header
let url = URL(string: "Enter the url here")
Alamofire.upload(multipartFormData: { (multipartFormData) in
for document in files {
let fileName = NSUUID().uuidString
multipartFormData.append(document, withName: "documents", fileName:fileName ,mimeType: "image/jpeg") // You'll have to define the proper mime time for uploading other type of files. You may achieve it by creating a struct and storing the type of each file.
}
}, usingThreshold: UInt64.init(), to:url, method: .post, headers: header) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseJSON { response in
switch response.result {
case .success(_) : completion(true, nil)
case .failure(let error) :
print("Error in upload: \(error.localizedDescription)")
completion(false, nil)
}
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
completion(false,error)
}
}
}
typealias uploadHandler = (_ status :Bool,_ error :Error?)->()// Define this anywhere globally
While calling the function I'm passing an array of files, I converted different type of file to data and uploading them to server.
if you want to upload array of images than before calling the function .
var documents = [Data]()
for image in imageArray {
if let imgData = UIImageJPEGRepresentation(image, 1.0) {
documents.append(document)
}
}
Now you can call the upload function and pass the documents and listen to completion Handler.
Good luck .

Resources