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"
}
Related
i have some problem request video, image to server,
about 3MB video, 10kb image * 2
it's long time
about 30~40s
but, when quick case, just 4s
i don't know what happen.
how i reduce form data body
body.append("profilePhoto", "profilePhoto");
body.append("profileVideo", "profileVideo");
body.append("thumbnailPhoto", "thumbnailPhoto");
body.append("profileMessage", profileMessage);
body.append("userIdentifier", userIdentifier);
body.append("firebaseToken", nickname);
body.append("nickname", nickname);
body.append("dob", dob);
body.append("bodyType", bodyType);
body.append("sexType", sexType);
body.append("lat", lat);
body.append("lng", lng);
body.append("loginType", loginType);
const { data: signUpResult } = await client.post("/signup", body, {
headers: { "content-type": "multipart/form-data" },
});
I have created a swagger specification which produces "application/zip"
/config:
get:
produces:
- application/zip
responses:
200: # OK
description: All config files
schema:
type: string
format: binary
I have implemented the handlers for this endpoint but I get this error
http: panic serving 127.0.0.1:20366: applicationZip producer has not yet been implemented
This error is coming from this code
func NewSampleAPI(spec *loads.Document) *SampleAPI {
return &SampleAPI{
...
ApplicationZipProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
return errors.NotImplemented("applicationZip producer has not yet been implemented")
}),
After investigating this error my findings are that we need to implement something like this
api := operations.NewSampleAPI(swaggerSpec)
api.ApplicationZipProducer = func(w io.Writer, data interface{}) error {
...
}
So my question is that
what should we put in this Producer and why is it necessary to implement this because there is no implementation for "application/json" ?
Is "application/json" Producer is implemented by default and we need to implement other producers?
Note: I am using swagger "2.0" spec
Since you have used "application/zip" as response content then you might have implemented the backend code which might be returning io.ReadCloser.
Then your Producer will look like this
api.ApplicationZipProducer = runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
if w == nil {
return errors.New("ApplicationZipProducer requires a writer") // early exit
}
if data == nil {
return errors.New("no data given to produce zip from")
}
if zp, ok := data.(io.ReadCloser); ok {
b, err := ioutil.ReadAll(zp)
if err != nil {
return fmt.Errorf("application zip producer: %v", err)
}
_, err = w.Write(b)
return nil
}
return fmt.Errorf("%v (%T) is not supported by the ApplicationZipProducer, %s", data, data)
})
This will parse the data interface into io.ReadCloser and read data from it and then it will fill into the io.Writer
Note: If your main purpose is just send file as attachment then you should use "application/octet-stream" and its producer is implemented by default
I am using apollo-upload-client for file upload. The payload it creates is:
{
query: mutation($file: Upload!) { singleUpload(file: $file) { id } },
variables: {
file: File // a.txt
}
}
and the payload is:
...
{ "0": ["variables.file"] }
--------------------------cec8e8123c05ba25
Content-Disposition: form-data; name="0"; filename="a.txt"
Content-Type: text/plain
Is there a way for the payload name="0" property to equal the one stated in the mutation for example
{
query: mutation($file: Upload!) { singleUpload(file: $file) { id } },
variables: {
someCustomName: File // a.txt
}
}
and the payload to be :
...
{ "0": ["variables.someCustomName"] }
--------------------------cec8e8123c05ba25
Content-Disposition: form-data; name="someCustomName"; filename="a.txt"
Content-Type: text/plain
Is there a way to modify this name="0" property? Thanks!
Mapping entry points to used file variable name ($file)
... you can rename it like:
mutation($someFile: Upload!) {
singleUpload(file: $someFile) {
id
... and of course variables: {someFile: File}
In my app (sails 0.12.0) I want to extend a limit of bytes send upon POST request. So I've followed the comments in this stackoverflow question
var skipper = require('skipper');
skipper.limit = 1024*1024*100;
middleware: {
bodyParser: skipper
}
I still get an error:
"data": {
"code": "E_EXCEEDS_UPLOAD_LIMIT",
"name": "Upload Error",
"maxBytes": 15000000,
"written": 15007474,
"message": "Upload limit of 15000000 bytes exceeded (15007474 bytes written)"
}
I've also tried to add the code below directly under module.exports.http and then I've tried to add it in the middleware only.
bodyParser: (function () {
var opts = {limit:'50mb'};
var fn;
// Default to built-in bodyParser:
fn = require('skipper');
return fn(opts);
})
My question is: Why none of these codes work and how can I increase the limit. The solution can be not elegant.
Everything that you need - set
maxBytes
attribute in object of options to upload() method of skipper Upstream.
req.file('image').upload({maxBytes: 50000000}, function (err, uploadedFiles) {
if (err) return res.serverError(err.message);
if(uploadedFiles.length > 0) {
// do with uploaded images what you want
.....
}
});
I have problem very similar to this PDF Blob - Pop up window not showing content, but I am using Angular 2. The response on question was to set responseType to arrayBuffer, but it not works in Angular 2, the error is the reponseType does not exist in type RequestOptionsArgs. I also tried to extend it by BrowserXhr, but still not work (https://github.com/angular/http/issues/83).
My code is:
createPDF(customerServiceId: string) {
console.log("Sending GET on " + this.getPDFUrl + "/" + customerServiceId);
this._http.get(this.getPDFUrl + '/' + customerServiceId).subscribe(
(data) => {
this.handleResponse(data);
});
}
And the handleResponse method:
handleResponse(data: any) {
console.log("[Receipt service] GET PDF byte array " + JSON.stringify(data));
var file = new Blob([data._body], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
I also tried to saveAs method from FileSaver.js, but it is the same problem, pdf opens, but the content is not displayed. Thanks
I had a lot of problems with downloading and showing content of PDF, I probably wasted a day or two to fix it, so I'll post working example of how to successfully download PDF or open it in new tab:
myService.ts
downloadPDF(): any {
return this._http.get(url, { responseType: ResponseContentType.Blob }).map(
(res) => {
return new Blob([res.blob()], { type: 'application/pdf' })
}
}
myComponent.ts
this.myService.downloadPDF().subscribe(
(res) => {
saveAs(res, "myPDF.pdf"); //if you want to save it - you need file-saver for this : https://www.npmjs.com/package/file-saver
var fileURL = URL.createObjectURL(res);
window.open(fileURL); / if you want to open it in new tab
}
);
NOTE
It is also worth mentioning that if you are extending Http class to add headers to all your requests or something like that, it can also create problems for downloading PDF because you will override RequestOptions, which is where we add responseType: ResponseContentType.Blob and this will get you The request body isn't either a blob or an array buffer error.
ANGULAR 5
I had the same problem which I lost few days on that.
Here my answer may help others, which helped to render pdf.
For me even though if i mention as responseType : 'arraybuffer', it was unable to take it.
For that you need to mention as responseType : 'arraybuffer' as 'json'.(Reference)
Working code
downloadPDF(): any {
return this._http.get(url, { responseType: 'blob' as 'json' }).subscribe((res) => {
var file = new Blob([res], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
}
Referred from the below link
https://github.com/angular/angular/issues/18586
Amit,
You can rename the filename by adding a variable to the end of the string
so saveAs(res, "myPDF.pdf");
Becomes
saveAs(res, "myPDF_"+someVariable+".pdf");
where someVariable might be a counter or my personal favorite a date time string.
This worked for me
var req = this.getPreviewPDFRequest(fd);
this.postData(environment.previewPDFRFR, req).then(res => {
res.blob().then(blob => {
console.clear();
console.log(req);
console.log(JSON.stringify(req));
const fileURL = URL.createObjectURL(blob);
window.open(fileURL, '', 'height=650,width=840');
})
});
Server side (Java/Jetty) : REST service that returns a File Response
The File Response itself will automatically be parsed into a pdf blob file by Jetty (because of the annotation #Produces("application/pdf") ), in other to be send to and read by the web client
#GET
#Path("/download-pdf/{id}")
#Produces("application/pdf")
public Response downloadPDF(#ApiParam(value = "Id of the report record")
#PathParam("id") Long id) {
ResponseBuilder response = null;
try {
PDFReportService service = new PDFReportService();
File reportFile = service.getPDFReportFile(id);
response = Response.ok((Object) reportFile);
response.header("Content-Disposition","attachment; filename="+reportFile.getName());
return response.build();
} catch (DomainException e) {
response = Response.serverError().entity("server.error");
}
return response.build();
}
Client side code (angular 2) : grab the blob and print it in a new browser tab
The key is to insure that you read the request reponse as a blob (as the server returned a blob; in my case)
Now, I tried so hard but I finally figured out that Angular 2 has not implemented any function to handle blob responses (neither res['_body'], nor res.blob() worked for me)
So I found no other workaround than using JQuery ajax to perform that file blob request, like following:
public downloadPDFFile() {
let fileURL = serverURL+"/download-pdf/"+id;
let userToken: string = your_token;
showWaitingLoader();
$.ajax({
url: fileURL,
cache: false,
headers: {
"Content-Type": "application/json",
"Authorization": "Basic " + userToken
},
xhrFields: {
responseType: 'blob' //Most important : configure the response type as a blob
},
success: function(blobFile) {
const url = window.URL.createObjectURL(blobFile);
window.open(url);
stopWaitingLoader();
},
error: function(e){
console.log("DOWNLOAD ERROR :", e);
}
});
}