Upload file to mailgun server with swift - swift

I am trying to attach file to Mailgun. Here is my curl command:
curl -s --user 'api:key-XXX'
https://api.mailgun.net/v3/sandbox---.mailgun.org/messages
-F from='Excited User<mailgun#sandbox---.mailgun.org>'
-F to=mail#gmail.com
-F subject='Hello'
-F text='Testing some Mailgun awesomness!'
-F attachment='#/Users/.../mytestfile.jpeg'
Result: Ok, the file has been attached to the message and been successfully transferred.
After that, I tried doing it with URLRequest:
guard let filePath = Bundle.main.url(forResource: "domru", withExtension: "jpeg") else {
print(">>>can't find path")
return
}
let parameters: HTTPHeaders = [
"from": "Excited User<mailgun#sandbox---.mailgun.org>",
"to": "mail#gmail.com",
"subject": "hello",
"text": "my text message" ]
let data = encodeRequest(parameters: parameters, attachment: filePath)
var request = URLRequest(url: URL(string:"https://api.mailgun.net/v3/sandbox---.mailgun.org/messages")!)
request.httpMethod = "POST"
request.httpBody = data.data(using: .utf8)
request.addValue("Basic \(credentialData!)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request, completionHandler:{(data, response, error) in
if let err = error {
print(err)
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
private func encodeRequest(parameters:[String:String], attachment: URL) -> String {
var result = ""
for (key, value) in parameters {
result.append("\(key)="+"\(value)".addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)! + "&")
}
result.append("attachment="+attachment.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!)
return result
}
Result: The text of the message has been delivered, but the file has not been attached.
I also tried to solve that with Alamofire:
let imageData = UIImageJPEGRepresentation(UIImage(named:"domru.jpeg")!, 1)
let header : HTTPHeaders = ["Authorization":"Basic \(credentialData!)"]
Alamofire.upload(
// parameters are the same as in the previous part of code
multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key)
}
multipartFormData.append(imageData!, withName: "domru", fileName: "domru.jpeg", mimeType: "image/jpeg")
},
to: "https://api.mailgun.net/v3/sandbox---.mailgun.org/messages",
method: .post,
headers: header,
encodingCompletion: { (result) in
debugPrint(result)
}
Result the same: The attachment has not been attached to the message.
How attach a file to a message in this case?

For anyone still working on this. It seems that the MailGun API looks for the name to be attachment on each of the attachments included so this line:
multipartFormData.append(imageData!, withName: "domru", fileName: "domru.jpeg", mimeType: "image/jpeg")
would need to be changed to:
multipartFormData.append(imageData!, withName: "attachment", fileName: "domru.jpeg", mimeType: "image/jpeg")
not sure if that entirely fixes it

Related

"Invalid Form Data" in file upload on slack API

I'm working on a project where I need to upload files to channels in a slack workspace using slack's API. This is how I'm reading the file contents:
func readFile(localFilePath: String) -> Data? {
var contents: Data
let url = URL(fileURLWithPath: localFilePath)
do {
contents = try Data(contentsOf: url)
} catch {
return nil
}
return contents
}
However, this is what I get on slack:
I have also tried reading the file as a String, like this:
contents = try String(contentsOf: url, encoding: .ascii)
This way slack appears to recognize it's a PDF, but it's blank (see image below).
If I add an explicit Content-Type header and set it to multipart/form-data;, I get an invalid_form_data error in response.
Here's the request code:
let reqHeaders: HTTPHeaders = [
"Authorization": "",
"Accept": "application/json",
"Content-Type": "multipart/form-data;",
]
let params: [String:Any] = [
"channels": channelSelections.joined(separator: ","),
"content": fileData,
"initial_comment": messageText,
"filename": filename
]
AF.request(url, method: .post, parameters: params, headers: reqHeaders).responseJSON { response in
switch response.result {
case .success:
if let data = response.data {
do {
let json = try JSON(data: data)
if json["ok"].boolValue {
// some code
} else {
print("\n\n\n\(json)\n\n\n")
}
}
catch{
print("JSON Error")
}
}
case let .failure(error):
print(error)
}
}
What's the correct way to read the file and send as payload to slack's API?

Ximilar POST request with image URL and headers

I am trying to run a post request to send an image to the ximilar app. Not even sure if my request is 100% correct. Each time I get the same response of:
https://api.ximilar.com/recognition/v2/classify/ } { Status Code: 400, Headers {
"Content-Type" = (
"application/json"
);
Date = (
"Mon, 12 Apr 2021 04:02:58 GMT"
);
Server = (
"nginx/1.16.1"
);
"Strict-Transport-Security" = (
"max-age=31536000"
);
Vary = (
"Accept, Origin"
);
allow = (
"POST, OPTIONS"
);
"referrer-policy" = (
"same-origin"
);
"x-content-type-options" = (
nosniff
);
"x-frame-options" = (
DENY
);
} }
{
records = (
"Expected a list of items but got type \"str\"."
);
}
My code: (not sure if the body values are written right.)
let url = URL(string: "https://api.ximilar.com/recognition/v2/classify/")!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Token __MyTOKEN__", forHTTPHeaderField: "Authorization")
let body = [
"task_id" : "c03c288b-a249-4b17-9f63-974c2f30beb9",
"records" : "https://www.sticky.digital/wp-content/uploads/2013/11/img-6.jpg"
]
let bodyData = try? JSONSerialization.data(withJSONObject: body, options: [] )
request.httpMethod = "POST"
request.httpBody = bodyData
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()
I want to be able to output the JSON response in the console.
Here is the code in curl which works fine:
curl -H "Content-Type: application/json" -H "authorization: Token __API_TOKEN__" https://api.vize.ai/v2/classify -d '{"task_id": "0a8c8186-aee8-47c8-9eaf-348103feb14d", "version": 2, "descriptor": 0, "records": [ {"_url": "__IMAGE URL HERE__" } ] }'
The server is reporting an error for records:
Expected a list of items but got type "str"
It is saying that it was expecting an array of items for records, but only received a string. In your curl, records is an array of dictionaries with a single key, _url, but in Swift the value is just a string, consistent with what the error reported. You also supply version and descriptor in curl, but not in the Swift example.
Thus, you might try:
let body: [String: Any] = [
"task_id" : "c03c288b-a249-4b17-9f63-974c2f30beb9",
"version": 2,
"descriptor": 0,
"records" : [
[
"_url": "https://www.sticky.digital/wp-content/uploads/2013/11/img-6.jpg"
]
]
]
Alternatively, you might define Encodable types as outlined in Encoding and Decoding Custom Types:
struct XimilarRequest: Encodable {
let taskId: String
let version: Int
let descriptor: Int
let records: [XimilarRecord]
}
struct XimilarRecord: Encodable {
let url: URL
enum CodingKeys: String, CodingKey {
case url = "_url"
}
}
Then you can do:
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
let imageUrl = URL(string: "https://www.sticky.digital/wp-content/uploads/2013/11/img-6.jpg")!
let ximilarRequest = XimilarRequest(
taskId: "c03c288b-a249-4b17-9f63-974c2f30beb9",
version: 2,
descriptor: 0,
records: [XimilarRecord(url: imageUrl)]
)
request.httpBody = try encoder.encode(ximilarRequest)
Hi here is the example that should work for you (fill the Auth token and _url of the image):
import Foundation
let headers = [ "Content-Type": "application/json", "authorization": "Token __API_TOKEN__" ] let parameters = [ "task_id": "0a8c8186-aee8-47c8-9eaf-348103feb14d", "version": 2, "records": [["_url": "__IMAGE URL HERE__"]] ] as [String : Any]
let postData = JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "https://api.ximilar.com/recognition/v2/classify")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data
let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) {
print(error) } else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse) } })
dataTask.resume()

Multipart form data upload with multiple images + parameters (Alamofire 5.2)

I am trying to upload multiple images and parameters to the server using 'Alamofire 5.2)'
This is what the server expects
[{"key":"media1","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]},
[{"key":"media2","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]},
[{"key":"media3","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]},
{"key":"model","value":"{\"emp_Id\": \"6\",\"device_Identifier\":\"Device 01\",\"timestamp\":\"123\,”\description\”:\”description\",”handoverStatus”:”false”}","type":"text"}]
//Please note the array part
This is how i add picked image to an image array
var dictImage : [UIImage]?
if pickedImage.pngData() != nil
{
dictImage!.append(pickedImage)
}
This is how im trying to call the alamofire upload
func xyz()
{
let param : [String: Any] = [
"emp_Id" : "",
"device_Identifier" : "",
"timestamp" : "",
"description" : "",
"handoverStatus" : ""
]
let urlString = URL(string: APIDetails.BaseUrl + "api/123")!
let url = (try? URLRequest(url: urlString, method: .post, headers: APIDetails.header))!
AF.upload(multipartFormData: { (multipartData) in
let img1 = arrImage![0]
let img2 = arrImage![1]
let img3 = arrImage![2]
multipartData.append(img1.pngData(), withName:"media1", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
multipartData.append(img2.pngData(), withName:"media2", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
multipartData.append(img3.pngData(), withName:"media3", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
for (key, value) in param {
multipartData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
}, with: url)
}
How do i upload every image and its details as a dictionary?
How do i add upload progress and result that shows if anything failed or not ?
I tried with
}, with: url,encodingCompletion:{
EncodingResult in
switch EncodingResult{
case .success(let upload, _, _):
upload.responseJSON { response in
debugPrint("SUCCESS RESPONSE: \(response)")
}
case .failure(let encodingError):
print("ERROR RESPONSE: \(encodingError)")
} })
But it gives error Extra argument 'encodingCompletion' in call
(This is my first time trying to upload multipart form data, the code/part of it was taken from another answer)
try this code, I have not tested in Alamofire(5.2)
let baseUrl = "your URL"
let fullUrl = baseUrl + strUrl
var headers : HTTPHeaders = HTTPHeaders(["Content-type" : "multipart/form-data"])
if let header = header{
headers = header
}
guard let url = try? URLRequest(url: fullUrl, method: .post, headers: headers) else {return}
AF.upload(multipartFormData: { (multipartData) in
for i in 0 ..< arrImage.count{
if let imageData = arrImage[i].pngData(){
let mediaName = "media\(i + 1)"
multipartData.append(imageData, withName:mediaName, fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
}
}
for (key, value) in parameter {
multipartData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
}, to: url).responseJSON(queue: .main, options: .allowFragments) { (response) in
switch response.result{
case .success(let value):
print("Json: \(value)")
case .failure(let error):
print("Error: \(error.localizedDescription)")
}
}.uploadProgress { (progress) in
print("Progress: \(progress.fractionCompleted)")
}

How to upload pdf file as multi part form data in alamofire?

I have to upload pdf file as multi part form data.
I read Alamofire/Usage.md(Uploading Data to a Server)
So I write below code.
extension ViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let url = urls.first else { return }
print(url) // file:///private/var/mobile/Containers/Data/Application/69C5B45A-AA29-46D2-909C-2A1A5A68C10F/tmp/com.test.test-Inbox/D5100_EN.pdf
do {
let data = try Data(contentsOf: url)
print(data) // 10899227 bytes
AF.upload(multipartFormData: { multipartFormData in
multipartFormData.append(data, withName: "pdf")
}, to: "https://myurl.com")
.responseJSON { response in
debugPrint(response) // message = "Required request part 'file' is not present"
}
} catch {
print(error)
}
}
}
But it was filed.
How can I upload pdf file as multi part form data in alamofire?
You need to specify the mimeType :
multipartFormData.append(pdfData, withName: "pdfDocuments", fileName: "pdf", mimeType:"application/pdf")
Updated
According to the error you must understand that server is expecting a pdf with name "file"
try this:
multipartFormData.append(pdfData, withName: "file", fileName: "file", mimeType:"application/pdf")
You probably need to specify MimeType. Try updating your request to
AF.upload(multipartFormData: { multipartFormData in
multipartFormData.append(data, withName: "name", fileName: "fileName", mimeType: "application/pdf")
}, to: "https://myurl.com")
.responseJSON { response in
debugPrint(response) // message = "Required request part 'file' is not present"
}

Get "Parse Error" when sending request to YouTube Data API to start a resumable upload session

https://developers.google.com/youtube/v3/guides/using_resumable_upload_protocol
I try to follow this guide to start a resumable session.
But server returns this response :
success({
error = {
code = 400;
errors = (
{
domain = global;
message = "Parse Error";
reason = parseError;
}
);
message = "Parse Error";
};
})
Swift 5/ Alamofire 5
let filePath = "/Users/lucas/Documents/test.mp4"
do{
let attr = try FileManager.default.attributesOfItem(atPath: filePath)
let dict = attr as NSDictionary
let fileSize = dict.fileSize()
let dicSnippest = ["title": "TestVideo",
"description": "_description_",
"tags":"gundam",
"catagoryId":"22"]
let dicStatus = ["privacyStatus": "private",
"embedded": "True",
"license": "youtube"]
let sessionParam = ["snippet": dicSnippest,
"status": dicStatus
]
let targetUrl = "https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status"
let sessionHeaders = HTTPHeaders([
"Authorization": "Bearer \(token)",
"Content-Type": "application/json; charset=utf-8",
"Content-Length": "\(fileSize)",
"X-Upload-Content-Length": "\(fileSize)",
"X-Upload-Content-Type": "application/octet-stream"])
print("token: \(token)")
print("\(filePath): \(fileSize) bytes")
AF.request(targetUrl, method: .post, parameters: sessionParam, encoding: URLEncoding.default, headers: sessionHeaders).responseJSON {
response in
print("Http Status Code: \(String(describing: response.response?.statusCode))")
switch (response.result)
{
case .success(_):
print("Get Server Response!!")
print(response.result)
break
case .failure(let error):
print("Failure!!")
print(error)
break
}
}
} catch {
}
BTW, I modify the parameter of AF.request - encoding from URLEncoding.httpBody to JSONEncoding.default.
This error is happened:
responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.inputDataNilOrZeroLength)