Append data to post body and upload to api - ios

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

Related

Swift: Background upload using URLSession

I'm trying to upload files to the s3 bucket using URLSession. I understood that to upload files in the background, I need to use uploadTask(with:fromFile:) method as mentioned here. So I am performing below steps to upload the file.
Create a background URLSession
lazy var session: URLSession = {
let bundleIdentifier = Bundle.main.bundleIdentifier!
let config = URLSessionConfiguration.background(withIdentifier: bundleIdentifier + ".background")
config.sharedContainerIdentifier = bundleIdentifier
config.sessionSendsLaunchEvents = true
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
Generate request with multipart data
Data Model
struct UploadRequest {
let destinationUrl: URL
let sourceURL: URL
let params: [String: String]
let fileName: String
let mimeType: String
}
private func requestAndPath(for
uploadParam: UploadRequest) -> (request: URLRequest,
filePath: URL)? {
// Create an empty file and append header, file content and footer to it
let uuid = UUID().uuidString
let directoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
let fileURL = directoryURL.appendingPathComponent(uuid)
let filePath = fileURL.path
FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil)
let file = FileHandle(forWritingAtPath: filePath)!
let boundary = UUID().uuidString
let newline = "\r\n"
do {
let partName = "file"
let data = try Data(contentsOf: uploadParam.sourceURL)
// Write boundary header
var header = ""
header += "--\(boundary)" + newline
header += "Content-Disposition: form-data; name=\"\(partName)\"; filename=\"\(uploadParam.fileName)\"" + newline
for (key, value) in uploadParam.params {
header += "Content-Disposition: form-data; name=\"\(key)" + newline
header += newline
header += value + newline
}
header += "Content-Type: \(uploadParam.mimeType)" + newline
header += newline
let headerData = header.data(using: .utf8, allowLossyConversion: false)
// Write data
file.write(headerData!)
file.write(data)
// Write boundary footer
var footer = ""
footer += newline
footer += "--\(boundary)--" + newline
footer += newline
let footerData = footer.data(using: .utf8, allowLossyConversion: false)
file.write(footerData!)
file.closeFile()
let contentType = "multipart/form-data; boundary=\(boundary)"
var urlRequest = URLRequest(url: uploadParam.destinationUrl)
urlRequest.httpMethod = "POST"
urlRequest.setValue(contentType, forHTTPHeaderField: "Content-Type")
return (urlRequest, fileURL)
} catch {
debugPrint("Error generating url request")
}
return nil
}
Upload file
func uploadFile(request: UploadRequest) {
if let reqPath = requestAndPath(for: uploadRequest) {
let task = session.uploadTask(with: reqPath.request,
fromFile: reqPath.filePath)
task.resume()
}
}
When I call the uploadFile method, the delegate didSendBodyData is called once and the control goes to didCompleteWithError with error as nil. But the file is not uploaded to the s3 bucket. What could be the issue?
I am able to upload the file using Alamofire but since Alamofire doesn't support background upload, I would like to fallback to URLSession
Upload using Alamofire (default)
AF.upload(multipartFormData: { multipartFormData in
for (key, value) in uploadRequest.params {
if let data = value.data(using: String.Encoding.utf8, allowLossyConversion: false) {
multipartFormData.append(data, withName: key)
}
}
multipartFormData.append(
uploadRequest.sourceURL,
withName: "File",
fileName: uploadRequest.fileName,
mimeType: uploadRequest.mimeType
)
}, with: urlRequest).responseData { response in
if let responseData = response.data {
let strData = String(decoding: responseData, as: UTF8.self)
debugPrint("Response data \(strData)")
} else {
debugPrint("Error is \(response.error)")
}
}.uploadProgress { progress in
debugPrint("Progress \(progress)")
}
I made changes in the request body and wrote the data to the file and used uploadTask(with:fromFile:) method using the background session. urlSessionDidFinishEvents(forBackgroundURLSession:) will be called once the upload is completed when the app is in the background.
private func requestAndPath(for
uploadParam: UploadRequest) -> (request: URLRequest,
filePath: URL)? {
let uuid = UUID().uuidString
let directoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
let fileURL = directoryURL.appendingPathComponent(uuid)
let filePath = fileURL.path
FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil)
let file = FileHandle(forWritingAtPath: filePath)!
let boundary = generateBoundary()
let lineBreak = "\r\n"
var body = Data()
for (key, value) in uploadParam.params {
body.append("--\(boundary + lineBreak)")
body.append("Content-Disposition: form-data; name=\"\(key)\"\(lineBreak + lineBreak)")
body.append("\(value + lineBreak)")
}
do {
let data = try Data(contentsOf: uploadParam.sourceURL)
body.append("--\(boundary + lineBreak)")
body.append("Content-Disposition: form-data; name=\"File\"; filename=\"\(uploadParam.fileName)\"\(lineBreak)")
body.append("Content-Type: \(uploadParam.mimeType + lineBreak + lineBreak)")
body.append(data)
body.append(lineBreak)
body.append("--\(boundary)--\(lineBreak)")
file.write(body)
file.closeFile()
var urlRequest = URLRequest(url: uploadParam.destinationUrl)
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
urlRequest.httpMethod = "POST"
return (urlRequest, fileURL)
} catch {
debugPrint("Error getting request")
}
return nil
}
func generateBoundary() -> String {
return UUID().uuidString
}
extension Data {
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
self.append(data)
}
}
}
Reference: https://stackoverflow.com/a/58246456/696465

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

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

Alamofire file upload getting error "JSON text did not start with array or object and option to allow fragments not set"

Below is my code referring this question answer
func createRequest(ResumeID: String, CandidateID: String, MediaName: String, FileExtension : String, MediaType : String) throws -> URLRequest {
let parameters = NSDictionary(objects: [ResumeID, CandidateID, MediaName, FileExtension,MediaType], forKeys: ["ResumeID" as NSCopying, "CandidateID" as NSCopying, "MediaName" as NSCopying, "FileExtension" as NSCopying, "MediaType" as NSCopying])
let boundary = generateBoundaryString()
let url = URL(string: "http://192.168.1.29/ColorsKit_New_Svr/WTCSvr.svc/WTCService?Id=6&SPName=Usp_RTN_IU_CandidateSubmissionResume")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let path1 = Bundle.main.path(forResource: "dummy-pdf_2", ofType: "pdf")!
request.httpBody = try createBody(with: parameters as? [String : String], filePathKey: "MediaContent", paths: [path1], boundary: boundary)
return request
}
private func createBody(with parameters: [String: String]?, filePathKey: String, paths: [String], boundary: String) throws -> Data {
var body = Data()
if parameters != nil {
for (key, value) in parameters! {
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.append("\(value)\r\n")
}
}
for path in paths {
let url = URL(fileURLWithPath: path)
let filename = url.lastPathComponent
let data = try Data(contentsOf: url)
let mimetype = mimeType(for: path)
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(filePathKey)\"; filename=\"\(filename)\"\r\n")
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(data)
body.append("\r\n")
}
body.append("--\(boundary)--\r\n")
return body
}
func sendMultipartRequest() {
let request: URLRequest
do {
request = try createRequest(ResumeID: "1", CandidateID: "1241124", MediaName: "dummy-pdf", FileExtension: "pdf", MediaType: "pdf")
} catch {
print(error)
return
}
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
// handle error here
print(error!)
return
}
// if response was JSON, then parse it
do {
let responseDictionary = try JSONSerialization.jsonObject(with: data!)
print("success == \(responseDictionary)")
// note, if you want to update the UI, make sure to dispatch that to the main queue, e.g.:
//
// DispatchQueue.main.async {
// // update your UI and model objects here
// }
} catch {
print(error)
let responseString = String(data: data!, encoding: .utf8)
print("responseString = \(String(describing: responseString))")
}
}
task.resume()
}
The Response I'm getting is:
Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start
with array or object and option to allow fragments not set."
UserInfo={NSDebugDescription=JSON text did not start with array or
object and option to allow fragments not set.} responseString =
Optional("No Records Found")
This is strange because Postman is giving correct response. Means there is something missing in the code only :(
Use Alamofire
let upload_url = "your url"
let fieldName = "UploadedFile"
let mimeType = "plain/text"
Alamofire.upload(multipartFormData: { multipartFormData in
//you can add multiple file
multipartFormData.append(fileData as Data, withName: fieldName, fileName: fileName, mimeType: mimeType)
}, to: upload_url, method: .post, headers: ["Authorization": "auth_token"],
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.response { [weak self] response in
guard let _ = self else {
return
}
debugPrint(response)
}
case .failure(let encodingError):
debugPrint("uploaderService error:\(encodingError)")
}
})
Use JSONSerialization as below
if let responseDictionary = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String {
...
}

Converting iPhone video to mp4 and upload to PHP Server

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

Resources