Search code examples
javascripttypescriptgoogle-drive-apimultipartgoogle-api-js-client

Uploading a file to Google Drive using gapi


I have followed this article as well as this SO answer on uploading a file to Google Drive which includes metadata, resulting in following code:

readonly BOUNDARY = '--4561564891651634213217'; //Randomly mashed in numbers

uploadToDrive(file: File /*<input type="file">.files[0]*/, fileName: string) {
    this.readFile(file)
        .then(base64File => {
          gapi.client.request({
            path: 'upload/drive/v3/files', method: 'POST', params: {
              uploadType: 'multipart'
            }, headers: {
              'Content-type': `multipart/related; boundary=${this.BOUNDARY}`,
              'Content-length': file.size
            }, body: this.formatMultipartBody(file, fileName, base64File)
          })
              .then(response => console.log('Upload success! ', response), error => console.error('Upload error! ', error));
        });
  }

private readFile(file: File): Promise<string> {
    const fileReader: FileReader = new FileReader();
    return new Promise(resolve => {
      fileReader.readAsBinaryString(file);
      fileReader.onload = (event: any) => resolve(btoa(fileReader.result));
    });
  }

private formatMultipartBody(file: File, fileName: string, base64File: string) {
    const delimiter = `\r\n--${this.BOUNDARY}\r\n`;
    const closeDelimiter = `\r\n--${this.BOUNDARY}--`;
    const metadata = {
      name: fileName, mimeType: file.type || 'application/octet-stream'
    };
    const body = `
    ${delimiter}
    Content-Type: application/json; charset=UTF-8\r\n\r\n
    Content-Transfer-Encoding: base64
    ${JSON.stringify(metadata)}
    ${delimiter}
    Content-Type: ${file.type || 'application/octet-stream'}\r\n
    ${base64File}
    ${closeDelimiter}
    `;
    return body;
  }

No matter which type of file I upload, I keep getting the following error:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "badContent",
    "message": "Unsupported content with type: application/octet-stream"
   }
  ],
  "code": 400,
  "message": "Unsupported content with type: application/octet-stream"
 }
}

This error appears even if the file has an extension (e.g. a hello.txt file containing the text "Hello, world!") and nowhere in the request is anything mentioning "application/octet-stream".


Solution

  • It seems that the Google Drive API is very picky about formatting the request body, this formatMultiPartBody method worked for me in the end:

    private formatMultipartBody(file: File, fileName: string, base64Data: string): string {
        const delimiter = `--${this.BOUNDARY}`;
        const closeDelimiter = `--${this.BOUNDARY}--`;
        const metadata = {
          name: fileName, mimeType: file.type || 'application/octet-stream'
        };
        const body = `
        \n${delimiter}\
        \nContent-Type: application/json; charset=UTF-8\
        \n\n${JSON.stringify(metadata)}\
        \n${delimiter}\
        \nContent-Type: ${file.type || 'application/octet-stream'}\
        \nContent-Transfer-Encoding: base64\      
        \n\n${base64Data}\
        \n${closeDelimiter}`;
        return body;
      }