如何使用 gapi 和可恢复上传将文件上传到 Google 驱动器?

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

我正尝试按照 this 指南在 Google 上进行可续传上传 Google Api。

这是我的代码,您可以看到它按照指南的要求发出了 2 个请求,第一部分创建元数据,然后我们使用第一个请求创建的会话开始上传文件的位置。

        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);
        });

有什么问题?

好吧,似乎 Google Api 库不喜欢将文件/字节数组作为其 gapi.client.request 的正文,他们正在将其截断

传递文件的正确方法是什么?我尝试了 body: file 和 body: reader.result 但结果相同

PS: gapi 已经通过 auth2 完全验证和初始化,我可以创建文件/目录。

编辑 1:

gapi 库只是对 FileArray 进行 jsoing,因此 JSON 函数将其修改为一个空对象,无法使其工作。必须缺少某些东西。

编辑 2:

我在没有 GAPI 的情况下也能正常工作,它可以正确上传文件,但我在使用 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);

如果有人有一些提示..

您必须使用 XMLHttpRequest 进行跨源 HTTP 请求。 gapi 客户端不支持 XMLHttpRequest。 (but there is this pull request that's been open for a while) 即使您没有在初始请求中发送文件二进制数据,您也必须对初始请求和上传文件的请求使用 XMLHttpRequest,以便返回位置 url 提供适当的响应 header (Access-Control-Allow-Origin: YOUR_URL) 并满足 CORS 要求。

这是一个很棒的tutorial about CORS and XMLHttpRequest,可能对转换您的请求很有用。

您可以使用您链接到的页面中描述的请求信息。 This example shows the request info but does not provide any info on getting the auth token. But this example does!

我能够使用以下代码成功上传文件:

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
}));

但是这里有一个更强大的 repo 可以处理所有这些:https://github.com/googledrive/cors-upload-sample

这是转换为 angular http 服务的 BMcV 解决方案。

const contentType = file.type || 'application/octet-stream';
const baseRoot = gapi['config'].get('googleapis.config').root;

const reader = new FileReader();

reader.onload = (e) => {

    const authHeader = `Bearer ${this.auth.currentUser.get().getAuthResponse().access_token}`;

    const metadataHeaders = {
        'Authorization': authHeader,
        'Content-Type': 'application/json',
        'X-Upload-Content-Length': file.size,
        'X-Upload-Content-Type': contentType
    };
    const metadataOptions = new RequestOptions({ headers: new Headers(metadataHeaders) });

    const url = `${baseRoot}/upload/drive/v3/files?uploadType=resumable`;

    const metadata = {
        'name': file.name,
        'mimeType': contentType,
        'Content-Type': contentType,
        'Content-Length': file.size
    };

    this.http.post(url, metadata, metadataOptions).subscribe(metadataRes => {

        const locationUrl = metadataRes.headers.get('Location');

        const uploadHeaders = {
            'Content-Type': contentType,
            'X-Upload-Content-Type': contentType
        };
        const uploadOptions = new RequestOptions({ headers: new Headers(uploadHeaders) });

        this.http.put(locationUrl, reader.result, uploadOptions).subscribe(uploadRes => {
            console.log(uploadRes.json());
        });
    }, (err) => {
        console.error(err);
    });
};
reader.readAsArrayBuffer(file);