Google Drive API: Correct way to upload binary files via the Multipart API

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:


For some reason the above content gets encoded as UTF-8 (I assume) when I try to POST it, enclosed in a multipart payload:

Content-Type: application/json

{"name": "F.ini"}

Content-Type: application/octet-stream

ÿþ                <-- in the outbound request, this gets UTF-8 encoded

Hex representation of the file that ultimately gets stored on server side:


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 =;
    boundary = "BOUNDARY";
    meta = '{"name": "' + + '"}';
    console.log(content.length);    // gives 2 as expected

    payload = [
        "--" + boundary, "Content-Type: application/json", "", meta, "", "--" + boundary,
        "Content-Type: application/octet-stream", "", content, "--" + boundary + "--"
    console.log(payload.length);    // say this gives n

    xhr = new XMLHttpRequest();"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)

My question is twofold:

  • Is there a way to avoid this automatic UTF-8 encoding? (Probably not, because this answer implies that the UTF-8 encoding is part of the XHR spec.)
  • If not, what is the correct way to "inform" the Drive API that my file content is UTF-8 encoded? I have tried these approaches, with no success:
    • appending ; charset=utf-8 or ; charset=UTF-8 to the binary part's Content-Type header
    • doing the same to the HTTP header on the parent request (Content-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

  • I want to keep things as light as possible, and
  • keeping aside reinventing-the-wheel and best-practices stuff, anything that is accomplished by a third party library should be doable via plain JS as well (this is just a fun exercise).

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?

Modification points:

  • Used new FormData() for creating the multipart/form-data.
  • Used reader.readAsArrayBuffer(file) instead of reader.readAsBinaryString(file).
  • Send the file as a blob. In this case, the data is sent as application/octet-stream.

Modified script:

file = picker.files[0];    // 'picker' is a file picker
reader = new FileReader();
reader.onload = function (e) {
    var content = new Blob([file]);
    var meta = {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();'post', '');
    xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
    xhr.onload = function() {


  • In this modified script, I put the endpoint and the header including the access token. So please modify this for your environment.
  • In this case, I used a scope of


In my environment, I could confirmed that this script worked. But if this didn't work in your environment, I'm sorry.


Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.