Log alamofire upload image request and response - swift

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.

Related

HOW TO POST IMAGE FILE WITH ALAMOFIRE 5.5

i want to POST an image file with multipartform using alamofire 5.5
im success to implement the GET method with this recently alamofire but get stuck with the POST method.
TIA
For Uploading images to server we need to use multipart request, you can upload the image to a server by using the following code snippet.
func uploadImage(imgType:String,imgData:Data,imageName:String){
// params to send additional data, for eg. AccessToken or userUserId
let params = ["userID":"userId","accessToken":"your accessToken"]
print(params)
AF.upload(multipartFormData: { multiPart in
for (key,keyValue) in params{
if let keyData = keyValue.data(using: .utf8){
multiPart.append(keyData, withName: key)
}
}
multiPart.append(imgData, withName: "key",fileName: imageName,mimeType: "image/*")
}, to: "Your URL",headers: []).responseJSON { apiResponse in
switch apiResponse.result{
case .success(_):
let apiDictionary = apiResponse.value as? [String:Any]
print("apiResponse --- \(apiDictionary)")
case .failure(_):
print("got an error")
}
}
}

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"

How to upload multipart form data inside a dictionary?

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

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

RESTful mechanism to upload video (and properties) to vimeo

The procedure to upload a video to Vimeo starts out very similarly as the one defined for Youtube, but only up to a point. Below I describe the steps that worked, and outline the last video-upload step which does not:
The Vimeo-upload dance begins when we pass the following parameters to trigger user authentication:
let authPath:String = "\(url_vauth)?response_type=\(response_type)&client_id=\(client_id)&redirect_uri=\(redirect_uri)&state=\(state)&scope=upload"
if let authURL:NSURL = NSURL(string: authPath) {
let request = NSURLRequest(URL: authURL)
webView.loadRequest(request) // opens a webpage in a webUIView
// once credentials are entered, google redirects back with the above uri + a temporary code
// we will exchange later for a token
// within AppDelegate, we have defined a way to handle this uri, which is to call
// processOAuthStep1Response(url)
Then, we process the returned response to extract an authorization code:
let components = NSURLComponents(URL: url, resolvingAgainstBaseURL: false)
var auth_code:String!
// the block below extracts the text that follows "code" in the return url
if let queryItems = components?.queryItems {
for queryItem in queryItems { // step through each part of url
if queryItem.name.lowercaseString == "code" {
auth_code = queryItem.value
break
} // end of if queryItem.name.lowercaseString
} // end of for
} // if let queryItems
With this authorization code, we then generate a token:
let getTokenPath:String = url_token
let grant_type = "authorization_code"
let header_plain = "\(client_id):\(client_secret)"
let string_plain = header_plain.dataUsingEncoding(NSUTF8StringEncoding)
let string_base64 = (string_plain?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)))! as String
let headers = ["Authorization": "basic \(string_base64)"] // note that string_base64 really needs to be in base64!
//print ("...header is: \(string_base64)")
let tokenParams = ["grant_type": grant_type, "code": receivedCode, "redirect_uri": redirect_uri, "scope": "public"]
let request = Alamofire.request(.POST, getTokenPath, parameters: tokenParams, encoding: .URL, headers: headers)
We use this token to generate a ticket:
request(.POST, url_getticket, parameters: ticketParams , encoding: .URL, headers: headers).responseJSON { response in
//print(response)
switch response.result {
case .Success(let data):
let json = JSON(data)
print (json)
let myticket = json["ticket_id"].stringValue
//let usage = json[("upload_quota")].stringValue
let htmlform = json[("form")].stringValue
let uploadlink = json[("upload_link_secure")].stringValue
print("......ticket is \(myticket)")
print("......form is \(htmlform)")
print("......upload link is \(uploadlink)")
case .Failure(let error):
print("Request failed with error: \(error)")
} // end of switch
Finally (and this is where things stop to a screeching halt) we are supposed to use this ticket to make a POST request to Vimeo. The problem is that this ticket is embedded in an html form that actually makes the upload request to Vimeo...Not very useful for the iOS platform where I'm trying to implement this. Ideally, I'd like implementing with Alamofire via an upload call like so:
let headers = ["Authorization": "Bearer \(token)"]
upload(
.POST,
"https://1511923767.cloud.vimeo.com/upload?ticket_id=#######&video_file_id=####&signature=####&v6=1&redirect_url=https%3A%2F%2Fvimeo.com%2Fupload%2Fapi%3Fvideo_file_id%3D498216063%26app_id%3D70020%26ticket_id%####%26signature%######",
headers: headers,
multipartFormData: { multipartFormData in
multipartFormData.appendBodyPart(data: videodata, name: "video", fileName: "bagsy.m4v", mimeType: "application/octet-stream")
},
encodingCompletion: { encodingResult in
switch encodingResult {
case .Success(let upload, _, _):
upload.progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in
dispatch_async(dispatch_get_main_queue()) {
let percent = (Float(totalBytesWritten) / Float(totalBytesExpectedToWrite))
//progress(percent: percent)
print ("................\(percent)")
}
}
upload.validate()
upload.responseJSON { response in
print(response)
callback(true)
}
case .Failure(_):
callback(false)
}
})
Needless to say, the chunk of code above does not work. Any guidance would be most appreciated.
Consider using the official Vimeo iOS Upload SDK. We made it public about 2 weeks ago. It's a Swift library that handles upload of video files to Vimeo servers. It does so using a background-configured NSURLSession (so uploads continue regardless of whether your app is in the foreground or background). Let us know if you have any questions. Note: I'm one of the authors of the library and I work at Vimeo.
The VimeoUpload README is pretty robust and should communicate all that you need to know. Lettuce know if you have additional questions though, or feel free to make a pull request.
The ticket is never manually used in an upload. You should always use the url, or html provided by the API.
If you see HTML, it's because you are not providing the "type" parameter. We default to a simple POST upload system as described here: https://developer.vimeo.com/api/upload/videos#simple-http-post-uploading
If you provide "type=streaming", Vimeo returns a "complete_url", which you must call after performing the streaming upload as described here: https://developer.vimeo.com/api/upload/videos#resumable-http-put-uploads