Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to upload files to Google drive using gapi and resumable uploads?

I'm trying to follow this guide for doing resumable uploads on Google Drive through Google Api's.

This is my code, you can see it makes 2 requests as the guide asks to, the first part creates the metadata, and then we use the location for starting uploading the files with the session created by the first request.

        const file = new File(['Hello, world!'], 'hello world.txt', { type: 'text/plain;charset=utf-8' });
        const contentType = file.type || 'application/octet-stream';

        const reqMetadata = gapi.client.request({
            'path': 'upload/drive/v3/files',
            'method': 'POST',
            'params': { 'uploadType': 'resumable' },
            'headers': {
                'X-Upload-Content-Type': file.type,
                'X-Upload-Content-Length': file.size,
                'Content-Type': 'application/json; charset=UTF-8'
            },
            'body': {
                'name': file.name,
                'mimeType': contentType,
                'Content-Type': contentType,
                'Content-Length': file.size
            }
        });

        reqMetadata.execute((respMetadata, rawRespMetadata: any) => {
            const locationUrl = JSON.parse(rawRespMetadata).gapiRequest.data.headers.location;

            const reader = new FileReader();

            reader.onload = (e) => {
                const reqFile = gapi.client.request({
                    'path': locationUrl,
                    'method': 'PUT',
                    'headers': {
                        'Content-Type': file.type,
                        'Content-Length': file.size
                    },
                    'body': reader.result
                });

                reqFile.execute(respFile => {
                    console.log(respFile);
                });
            };

            reader.readAsArrayBuffer(file);
        });

What's the problem?

Well, seems that the Google Api library does not like the File / byte array as body on their gapi.client.request and they're truncating it away see image

What's the correct way to pass the file? I tried both body: file and body: reader.result but same result

PS: gapi is already fully authenticated & initialized with auth2, I'm able to create files / directory.

EDIT 1:

gapi library is just jsoing the FileArray, therefore the JSON function modifies it to a empty object, no way to make it work.. something must be missing.

EDIT 2:

I made it work without the GAPI, it correctly uploads the file but I have some issues with the CORS

            reader.onload = (e) => {                    

                const authHeader = `Bearer ${this.auth.currentUser.get().getAuthResponse().access_token}`;
                const headers = new Headers({
                    'Authorization': authHeader,
                    'Content-Type': file.type
                });
                const options = new RequestOptions({ headers });
                const url = locationUrl;

                this.http.put(url, reader.result, options).subscribe(res => {
                    observer.next(res);
                }, (err) => {
                    observer.next({});
                });
            };

            reader.readAsArrayBuffer(file);

If somebody has some hints..

like image 780
Luca Trazzi Avatar asked Sep 11 '17 16:09

Luca Trazzi


1 Answers

You must use XMLHttpRequest to make a cross origin HTTP request. The gapi client does not support XMLHttpRequest. (but there is this pull request that's been open for a while) Even though you aren't sending the file binary data in the initial request, you must use XMLHttpRequest for both the initial and the request where the file is uploaded in order for the returned location url provide the appropriate response header (Access-Control-Allow-Origin: YOUR_URL) and satisfy the CORS requirements.

Here is a great tutorial about CORS and XMLHttpRequest that may be useful in converting your requests.

You can use the request info described in the page you linked to. This example shows the request info but does not provide any info on getting the auth token. But this example does!

I was able to successfully upload the file using the following code:

const file = new File(['Hello, world!'], 'hello world.txt', { type: 'text/plain;charset=utf-8' });
const contentType = file.type || 'application/octet-stream';
const user = gapi.auth2.getAuthInstance().currentUser.get();
const oauthToken = user.getAuthResponse().access_token;
const initResumable = new XMLHttpRequest();
initResumable.open('POST', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable', true);
initResumable.setRequestHeader('Authorization', 'Bearer ' + oauthToken);
initResumable.setRequestHeader('Content-Type', 'application/json');
initResumable.setRequestHeader('X-Upload-Content-Length', file.size);
initResumable.setRequestHeader('X-Upload-Content-Type', contentType);
initResumable.onreadystatechange = function() {
  if(initResumable.readyState === XMLHttpRequest.DONE && initResumable.status === 200) {
    const locationUrl = initResumable.getResponseHeader('Location');
    const reader = new FileReader();
    reader.onload = (e) => {
      const uploadResumable = new XMLHttpRequest();
      uploadResumable.open('PUT', locationUrl, true);
      uploadResumable.setRequestHeader('Content-Type', contentType);
      uploadResumable.setRequestHeader('X-Upload-Content-Type', contentType);
      uploadResumable.onreadystatechange = function() {
        if(uploadResumable.readyState === XMLHttpRequest.DONE && uploadResumable.status === 200) {
          console.log(uploadResumable.response);
         }
      };
      uploadResumable.send(reader.result);
    };
    reader.readAsArrayBuffer(file);
  }
};

// You need to stringify the request body containing any file metadata

initResumable.send(JSON.stringify({
  'name': file.name,
  'mimeType': contentType,
  'Content-Type': contentType,
  'Content-Length': file.size
}));

But there is a more robust repo for dealing with all this here: https://github.com/googledrive/cors-upload-sample

like image 54
BMcV Avatar answered Oct 04 '22 00:10

BMcV