Reputation: 1072
I'm trying to upload a binary file to Google Drive via the multipart upload API v3.
Here's the hex representation of the content of the file:
FF FE
For some reason the above content gets encoded as UTF-8 (I assume) when I try to POST it, enclosed in a multipart payload:
--BOUNDARY
Content-Type: application/json
{"name": "F.ini"}
--BOUNDARY
Content-Type: application/octet-stream
ÿþ <-- in the outbound request, this gets UTF-8 encoded
--BOUNDARY--
Hex representation of the file that ultimately gets stored on server side:
C3 BF C3 BE
The problem only occurs in the sending stage:
if I check the length of the content read from the file I always get 2;
regardless of whether I use FileReader#readAsBinaryString
or FileReader#readAsArrayBuffer
(producing a string with length 2, and an ArrayBuffer
with byteLength
2, respectively).
Here's the minimal code that I'm using to generate the multipart payload:
file = picker.files[0]; // 'picker' is a file picker
reader = new FileReader();
reader.onload = function (e) {
content = e.target.result;
boundary = "BOUNDARY";
meta = '{"name": "' + file.name + '"}';
console.log(content.length); // gives 2 as expected
payload = [
"--" + boundary, "Content-Type: application/json", "", meta, "", "--" + boundary,
"Content-Type: application/octet-stream", "", content, "--" + boundary + "--"
].join("\r\n");
console.log(payload.length); // say this gives n
xhr = new XMLHttpRequest();
xhr.open("POST", "/", false);
xhr.setRequestHeader("Content-Type", "multipart/related; boundary=" + boundary);
xhr.send(payload); // this produces a request with a 'Content-Length: n+2' header
// (corresponding to the length increase due to UTF-8 encoding)
};
reader.readAsBinaryString(file);
My question is twofold:
; charset=utf-8
or ; charset=UTF-8
to the binary part's Content-Type
headerContent-Type: multipart/related; boundary=blablabla, charset=utf-8
;
also tried replacing the comma with a semicolon)I need the multipart API because AFAIU the "simple" API
does not allow me to upload into a folder
(it only accepts a filename as metadata, via the Slug
HTTP header,
whereas the JSON metadata object in the multipart case allows a parent
folder ID to be specified as well).
(Just thought of mentioning this because the "simple" API handles things correctly
when I directly POST the File
(from the picker) or ArrayBuffer
(from FileReader#readAsArrayBuffer
) as the XHR's payload.)
I do not want to utilize any third-party libraries because
For the sake of completeness I tried uploading the same file via the GDrive web interface, and it got uploaded just fine; however the web interface seems to base64-encode the payload, which I would rather like to avoid (as it unnecessarily bloats up the payload, esp. for larger payloads which is my eventual goal).
Upvotes: 1
Views: 2918
Reputation: 201553
How about this modification?
new FormData()
for creating the multipart/form-data.reader.readAsArrayBuffer(file)
instead of reader.readAsBinaryString(file)
.application/octet-stream
.file = picker.files[0]; // 'picker' is a file picker
reader = new FileReader();
reader.onload = function (e) {
var content = new Blob([file]);
var meta = {name: file.name, mimeType: file.type};
var accessToken = gapi.auth.getToken().access_token;
var payload = new FormData();
payload.append('metadata', new Blob([JSON.stringify(meta)], {type: 'application/json'}));
payload.append('file', content);
xhr = new XMLHttpRequest();
xhr.open('post', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart');
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xhr.onload = function() {
console.log(xhr.response);
};
xhr.send(payload);
};
reader.readAsArrayBuffer(file);
https://www.googleapis.com/auth/drive
.In my environment, I could confirmed that this script worked. But if this didn't work in your environment, I'm sorry.
Upvotes: 2