How to upload multipart form data inside a dictionary? - swift

I am trying to upload a video to my rails API backend using Swift. To upload this I use Alamofire. The only problem is that the rails api is expecting this: {"video":{"clip": (form data)}}. The problem that I am having is storing the multipart form data is that it doesn't seem I can store it inside the second dictionary. I have tried params for my video, but it doesn't seem to work. Here is some of my code if it helps:
The Alamofire Request
AF.upload(
multipartFormData: { multipartFormData in
for (_, _) in params {
multipartFormData.append(self.videoURL, withName: "clip" , fileName: "clip.mp4", mimeType: "video/mp4")
}
multipartFormData.append("\(Id)".data(using: String.Encoding.utf8, allowLossyConversion: false)!, withName :"Id")
},
to: "http://10.0.0.2:3000/api/v1/videouploads.json", method: .post, headers: headers)
.response { resp in
print(resp)
}
What the server expects (pretty printed)
{
"video": {
"clip": (multipartformdata)
}
"id": (user id)
}

Try this:
multipartFormData.append(
self.videoURL,
withName: "video[clip]", // this is location in form' struct
fileName: "clip.mp4",
mimeType: "video/mp4"
)

Related

using Alamofire 5 to upload file to RocketChat server

I'm using Alamofire 5, and I'm trying to upload an image to a Rocket Chat server. The corresponding curl statement that I need to duplicate using AF is at the following link:
(link to documentation: https://docs.rocket.chat/api/rest-api/methods/rooms/upload)
I've been trying to upload using multipartFormData with no success. I've also attempted to bypass Alamofire altogether and use Swift URLSession. The best I can do is get the same error message from the server, which is "errorType": invalid-field."
My code as it stands right now:
let url = URL_MESSAGES + "rooms.upload/\(_group._id ?? "")"
let boundary = UUID().uuidString
let headers: HTTPHeaders = [
"X-Auth-Token": authToken,
"X-User-Id": me._id ?? "",
"Content-type":"multipart/form-data; boundary=\(boundary)"
]
if let data = image.jpeg(.medium) {
print(data.count)
AF.upload(
multipartFormData: { multipartFormData in
multipartFormData.append(data, withName: "image", fileName: "image.jpeg", mimeType: "image/jpeg")
},
to: url, method: .post , headers: header)
.response { resp in
print(resp)
}
.cURLDescription { description in
print(description)
}
.responseString { [weak self] (response) in
DispatchQueue.main.async {
if response.error == nil {
guard let data = response.data else {
return completion(true,[:])
}
if let json = try? JSON(data: data) {
let dictionaryIn = json.rawValue as! [String : Any]
if (self?.isSuccess(data: dictionaryIn))! {
completion(true,json.rawValue as! [String : Any])
}else{
completion(false,[:])
self?.handleError(data: dictionaryIn)
}
}
}else{
completion(false,[:])
self?.handleError(data: [:])
}
}
}
}
}
I think you're breaking the upload by trying to set your own boundary. Alamofire will that for you automatically. Try removing the header.
The above code works perfectly, when I changed this:
AF.upload(
multipartFormData: { multipartFormData in
multipartFormData.append(data, withName: "image", fileName: "image.jpeg", mimeType: "image/jpeg")
to:
AF.upload(
multipartFormData: { multipartFormData in
multipartFormData.append(data, withName: "file", fileName: "image.jpeg", mimeType: "image/jpeg")
Swapping out "image" for "file" fixed everything. I went with "image" because this is what every Alamofire tutorial I could find said to do. However, RocketChat requires it to be "file". It is in the documentation, but I didn't realize that's what it was telling me to do. Specifically, in the documentation, it says:
"Note: For some file types if uploading via curl you may need to set the mime type.
With some file types, curl will upload the file as application/octet-stream. You can pass a custom mime type like this: -F "file=#file.wav;type=audio/wav" "
I was trying -F "image=#file.wav;type=audio/wav" when translated from Alamofire. It needs to be: -F "file=#file.wav;type=audio/wav"

Log alamofire upload image request and response

I know there is a lot about this issue but I can't find exactly the one related to uploading. I'm using alamofire for my HTTP calls.
I'm trying to upload the image to Amazon S3 and getting an error, wanting to log request and response to see what exactly problem is.
All my requests are logging except Alamofire.upload. I added an extension to request and Aramofire.request is logging and now my problem is to log also Alamofire.upload.
As an example, I'm trying to log request and response for the following example.
Alamofire.upload(multipartFormData: { (multipartFormData) in
for (key, value) in params {
multipartFormData.append(value.data(using: String.Encoding.utf8, allowLossyConversion: false)!, withName: key)
}
if image != nil {
if let imgData = UIImageJPEGRepresentation(image!, 0.8) {
multipartFormData.append(imgData, withName: "file", fileName: "file.jpg", mimeType: "image/jpeg")
}
}
}, to: "amazon.s3.URL")
After digging into SessionManager class I found out the way how to log upload request and response.
I override the upload function.
Here is my example
class AlamofireManager: SessionManager {
override func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest {
let request = super.upload(data, with: urlRequest)
print("Request: \(request.debugDescription)")
print("Response: \(request.response.data)")
return request
}
}
You can also use this pod https://github.com/konkab/AlamofireNetworkActivityLogger and you can call it in applicationDidLaunch.. it will log everything pretty nice.

Alamofire photo upload with key of the file in body

I am able to upload a photo via Postman but in my iOS application it fails, and it is so weird that I still get a .success from encodingCompletion.
Here is part of my code
UpdateUserInfo
//
// Update user info
//
func updateUserInfo(){
var imageData: Data!
var url = ""
if let userId = KeychainWrapper.standard.string(forKey: USER_ID_KEY){
url = URL_USER_UPLOAD_PIC + userId
}
if pickedImage != nil{
imageData = UIImagePNGRepresentation(pickedImage)
//imageData = UIImageJPEGRepresentation(pickedImage!, 1.0)
}
let token = KeychainWrapper.standard.string(forKey: USER_LOGIN_TOKEN_KEY)!
let headers: HTTPHeaders = [
"Authorization": "Bearer \(token)"
]
if imageData != nil{
Alamofire.upload(multipartFormData: { (multipartFormData) in
multipartFormData.append(imageData!, withName: "fileset", fileName: "file.png", mimeType: "image/png")
}, to: url,
method: .post,
headers: headers,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
print("Donkey Success \(String(describing: upload.response?.statusCode))")
upload.responseString(completionHandler: { (response) in
debugPrint(response)
})
case .failure(let encodingError):
print(encodingError)
print("Donkey Fail")
}
})
}
}
and in my postman I have
Postman
My first question is why I am getting a .success if it fails to upload?
and my second question is, do I need to put the key "pic" (seen in Postman) somewhere in my request? if so where?
Thanks for any help in advance
In Post man Image Key is pic but in your code its "fileset"
change to
multipartFormData.append(imageData!, withName: "pic", fileName: "file.png", mimeType: "image/png")
And success is Result of encoding not uploading processs
EncodingResult is MultipartFormDataEncodingResult that Defines
whether the MultipartFormData encoding was successful and contains
result of the encoding as associated values. - Success: Represents a successful MultipartFormData encoding and contains the
new UploadRequest along with
streaming information. - Failure: Used to represent a failure in the MultipartFormData encoding and also
contains the encoding

Uploading image to third party service using alamofire and auth token

I have been trying to upload my image to a third party website using their api and alamofire to get some information back regarding the image that has been uploaded. I have to pass the auth token in with the request to get access to their resources, here is the instruction they have provided,
"Image should be specified by its url or uploaded with multipart form by parameter 'data'. You should use, respectively, GET and POST methods."
I have been trying to implement the upload method in 5 different ways but none of them seem to work I get a responseSerializationFailed(Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(Error Domain=NSCocoaErrorDomain Code=3840 \"Invalid value around character 2.\" UserInfo={NSDebugDescription=Invalid value around character 2.}))" error back here is one of the functions that takes care of the upload:
let headers: HTTPHeaders = [ "Authorization": "Bearer \(authToken)" ]
let URL = try! URLRequest(url: "url", method: .post, headers: headers)
Alamofire.upload(multipartFormData: { multipartFormData in
multipartFormData.append(imageData, withName: "signImg", fileName: "picture.png", mimeType: "image/png")
}, with: URL, encodingCompletion: {
encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
debugPrint("SUCCESS RESPONSE: \(response)")
}
case .failure(let encodingError):
// hide progressbas here
print("ERROR RESPONSE: \(encodingError)")
}
})
I also don't understand the meaning of the parameters used to append the multipartformdata such as withName, filetype, mimeType.
I would really appreciate if you could attach an explanation with the solution.

Pass an uploaded image directly from json file

I am trying to upload an image from my app to my heroku backend and then pass that to Stripe for verification. Here is my swift code to upload and pass the image:
#IBAction func registerAccountButtonWasPressed(sender: UIButton) {
let dob = self.dobTextField.text!.components(separatedBy: "/")
let URL = "https://splitterstripeservertest.herokuapp.com/account/create"
var imageData = UIImageJPEGRepresentation(photoID,1)
let params = ["year": UInt(dob[2])! as UInt] as [String : Any]
let manager = AFHTTPSessionManager()
let operation = manager.post(URL, parameters: params, constructingBodyWith: { (formData: AFMultipartFormData!) -> Void in
formData.appendPart(withFileData: imageData!, name: "file", fileName: "photoID.jpg", mimeType: "image/jpeg")
}, success: { (operation, responseObject) -> Void in
print(responseObject!)
}) { (operation, error) -> Void in
self.handleError(error as NSError)
}
}
I've deleted the list of params above out and left one for readability.
Is there a way to then receive this file and upload it to stripe without having to save it by passing the file parameter? like so:
Stripe::FileUpload.create(
{
:purpose => 'identity_document',
:file => params[file]
},
{:stripe_account => params[stripe_account]}
)
Also in the stripe docs it says to upload the file to 'https://uploads.stripe.com/v1/files' but then shows code to put in your backend, does Stripe::FileUpload.create do the uploading to stripe for me?
Any insight on either would be great thanks.
You need to first upload the file to Stripe's API using the "create file upload" call. You can then use the file upload's ID (file_...) to update the account's legal_entity.verification.document attribute.
(This process is explained here:
https://stripe.com/docs/connect/identity-verification#uploading-a-file
https://stripe.com/docs/connect/identity-verification#attaching-the-file)
Since the file is provided by the user, you have two choices for creating the file upload:
have your app upload the file to your backend server, then on your backend, use the file to create the file upload
create the file upload directly from the app (using your publishable API key), and send the resulting file_upload's ID (file_...) to your backend
Here's an example for creating file uploads client-side, using jQuery: https://jsfiddle.net/captainapollo/d8yd3761/.
You could do the same thing from your iOS app's code. Basically all you need to do is send a POST request to https://uploads.stripe.com/v1/files with an Authorization header with value Bearer pk_... (where pk_... is your publishable API key) and type multipart/form-data, with the file's contents as the request's body. This blog post should be helpful for sending multipart/form-data requests using Swift: http://www.kaleidosblog.com/how-to-upload-images-using-swift-2-send-multipart-post-request
Thanks to #Ywain I was able to come up with this solution for IOS Swift 4. I created the file upload directly from the app and retrieved the file_upload's ID to send to my backend to attach to the Connect Account. I did this by importing Alamofire and SwiftyJSON.
let heads = ["Authorization": "Bearer \(stripePublishableKey)"]
let imageData = image!.jpegData(compressionQuality: 1.0)
let fileName = "\(NSUUID().uuidString).jpeg"
Alamofire.upload(multipartFormData: { multipart in
multipart.append(imageData!, withName: "file", fileName: fileName, mimeType: "image/jpeg")
multipart.append(("identity_document").data(using: .utf8)!, withName :"purpose")
}, to: fileUrl!, method: .post, headers: heads) { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { answer in
let value = JSON(answer.value!)
let FileID = value["id"].stringValue
// Use fileID and Connect Account ID to create params to send to backend.
let params: [String: Any] = [
"acct_id": ConnectID!,
"fileID": FileID,
]
print("statusCode: \(String(describing: answer.response?.statusCode))")
}
case .failure(let encodingError):
print("multipart upload encodingError: \(encodingError)")
}
}