I am trying to upload a file + parameters to Google Drive via Swift 2/Alamofire. In the code below, i I change the line that says:
"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart"
to the following:
"https://www.googleapis.com/upload/drive/v3/files"
the file uploads to google without a name. Otherwise, the file upload fails with the same generic code:
Error Domain=com.alamofire.error Code=-6003 "Response status code was unacceptable: 400" UserInfo={NSLocalizedFailureReason=Response status code was unacceptable: 400}
I'd like to be able to upload the file with name and potentially other parameters as well. I know I'm mangling the multipart upload somehow, but I don't know what I'm doing wrong.
func postBinaryToGdriveSimple (token: String, callback: Bool -> Void){
var returnedId : String!
let path = NSBundle.mainBundle().pathForResource("drawing", ofType: "bin")
let bindata: NSData = NSData(contentsOfURL: NSURL(fileURLWithPath: path!))!
let parameters : [String: String] = ["title":"SomeFileName"]
let headers = ["Authorization": "Bearer \(token)"]
upload(
.POST,
"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart",
headers: headers,
multipartFormData: { multipartFormData in
// append file parameters to request
for (key, value) in parameters {
multipartFormData.appendBodyPart(data: value.dataUsingEncoding(NSUTF8StringEncoding)!, name: key)
}
// append binary file to request
multipartFormData.appendBodyPart(data: bindata, name: "upload", fileName: "drawing.bin", 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
switch response.result {
case .Success(let data):
print(response)
print("Validation Successful")
let json = JSON(data)
returnedId = json[("id")].stringValue
print("......id for uploaded file is \(returnedId)")
callback(true)
case .Failure(let error):
print(error)
print("Validation Bad")
callback(false)
}
}
case .Failure(_):
callback(false)
}
})
} // end of postBinaryToGdriveSimple
I wonder if there is something about the way Alamofire creates the multipart request that Google Drive is not liking. From the google api site, it seems like a request needs to have certain parameters that Alamofire may not be creating, such as Content-length and boundary settings...
POST /upload/drive/v3/files?uploadType=multipart HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer your_auth_token
Content-Type: multipart/related; boundary=foo_bar_baz
Content-Length: number_of_bytes_in_entire_request_body
--foo_bar_baz
Content-Type: application/json; charset=UTF-8
{
"name": "My File"
}
--foo_bar_baz
Content-Type: image/jpeg
JPEG data
--foo_bar_baz--
If so, what is the work-around?
Double check the API documentation for Google Drive.
It appears that the key field for the parameter is "name" (not "title").
If you want additional, custom file properties, restricted to the single app, add an "appProperties" to the json:
"appProperties": { "title": "whatever" }