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).
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.
©2020 All rights reserved.