"Invalid Form Data" in file upload on slack API - swift

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?

Related

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)

'Invalid type in JSON write (UIImage)' Alamofire API request

i am trying to send an image to an api using alamofire heres what i got:
var uploadedProfileImage: UIImage = UIImage()
let body: Parameters = [
"profilePic": uploadedProfileImage,
"name": "John Doe"
]
Alamofire.request(BASE_URL",method: .post,parameters: body,encoding: JSONEncoding.default).responseData { response in
debugPrint("All Response Info: \(response)")
if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) {
print("Data: \(utf8Text)")
}
}
so this is the code i am using uploadProfileImage has an image that the user picked from the library and my api receives a json body parameter in which it has profilePic which is off type file when i run this its giving me an error saying 'Invalid type in JSON write (UIImage)'.theres is also a terminating with uncaught exception of type NSException error. what am i doing wrong and how to fix it?
You can't upload image with
"profilePic": uploadedProfileImage
as it's used for types that are encode-able such as strings , you need
upload image to server using Alamofire
func uploadImageAndData(_ url:String,parameters1:Parameters,headers1:HTTPHeaders,images:[UIImage]){
Alamofire.upload(multipartFormData: { multipartFormData in
// import image to request
var i=0
for imageData in images {
// multipartFormData.append(self.resizeImage(image: imageData, targetSize: CGSize(width: 400, height:
multipartFormData.append(imageData.pngData()!, withName: "image", fileName: "image"+".jpeg", mimeType: "image/jpeg")
i += 1
}
for (key, value) in parameters1 {
multipartFormData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
} ,to: url,method:.post,
headers:[:],
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.uploadProgress(closure: { (Progress) in
print("Upload Progress: \(Progress.fractionCompleted)")
//SwiftSpinner.show(progress: Progress.fractionCompleted*100, title: "تحميل")
})
upload.responseJSON { response in
switch response.response?.statusCode {
case 200 :
break
case 400 :
break
case 401 :
break
case 302 :
break
case 500 :
break
default: break
}
}
return
case .failure(let encodingError):
}
})
}
var head = [String:Any]()
head["Accept"]="application/json"
head["Authorization"] = "Bearer \(tokenIfExists)"

Upload file to mailgun server with 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

Running Two Alamofire Requests in Sequence Swift

I am using Async library found here in order to wait for first alamofire request to complete before running my second from the ViewDidLoad. The library seems simple to use but I can never get the first request to wait. My code is as follows:
let group = AsyncGroup()
group.utility
{
self.getToken()
}
group.wait()
self.getDevices()
I would like the getToken function to complete the Alamofire request and its completion handler before moving on to the getDevices request. Both are very simple Alamofire requests.
EDIT:
This is the getToken request. The token variable is not getting updated with the alamofire response before second alamofire request is being called.
func getToken()
{
let httpheader: HTTPHeaders =
[
"Content-Type": "application/json"
]
// Dev
let param = [params here]
Alamofire.request("url", method: .post, parameters: param, encoding: JSONEncoding.default, headers:httpheader).validate()
.responseData { response in
switch response.result {
case .success:
if let data = response.data {
let xml = SWXMLHash.parse(data)
token = (xml["authResponse"] ["authToken"].element?.text)!
}
case .failure:
print ("error")
}
}
}
Your getToken looks more like:
func getToken(whenDone:(String?)->()) {
let httpheader: HTTPHeaders = [
"Content-Type": "application/json"
]
// Dev
let param = [params here]
Alamofire.request("url", method: .post, parameters: param, encoding: JSONEncoding.default, headers:httpheader).validate()
.responseData { response in
switch response.result {
case .success:
if let data = response.data {
let xml = SWXMLHash.parse(data)
token = (xml["authResponse"] ["authToken"].element?.text)!
whenDone(token)
}
case .failure:
print ("error")
whenDone(nil)
}
}
}
and the calling sequence just becomes:
getToken() { token ->
guard let token = token else {
return
}
getDevices()
}