如何使用gapi将图像上传到谷歌驱动器并做出react

IT技术 reactjs google-drive-api
2021-05-19 22:21:27

我正在尝试将图像上传到谷歌驱动器。我按照本教程上传文件,我可以上传一个简单的文件,但是当我尝试上传图像时,我的图像已损坏。实际上,文件已上传,但我想查看的图像已损坏。我知道问题出在请求的正文中,但我不知道在哪里,因为这是一种奇怪的方式(边界、分隔符等)。除了图像的内容外,一切正常,因此您只需要查看上传图像的 post 方法。这是我的代码:

import { Upload } from 'antd';
import React, { Component } from 'react';

var SCOPE = 'https://www.googleapis.com/auth/drive.file';
var discoveryUrl = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';


class App extends Component {
  state = {
    name: '',
    googleAuth: '',
    body: ''
  }
  componentDidMount(){
    var script = document.createElement('script');
    script.onload=this.handleClientLoad;
    script.src="https://apis.google.com/js/api.js";
    document.body.appendChild(script);
  }


  initClient = () => {
    try{
      window.gapi.client.init({
          'apiKey': "apikey",
          'clientId': "clientid",
          'scope': SCOPE,
          'discoveryDocs': [discoveryUrl]
        }).then(() => {
          this.setState({
            googleAuth: window.gapi.auth2.getAuthInstance()
          })
          this.state.googleAuth.isSignedIn.listen(this.updateSigninStatus);  
         document.getElementById('signin-btn').addEventListener('click', this.signInFunction);
         document.getElementById('signout-btn').addEventListener('click', this.signOutFunction);

      });
    }catch(e){
      console.log(e);
    }
  }


  signInFunction =()=>{
    console.log(this.state.googleAuth)
    this.state.googleAuth.signIn();
    console.log(this.state.googleAuth)
    this.updateSigninStatus()
  }

  signOutFunction =()=>{
    this.state.googleAuth.signOut();
    this.updateSigninStatus()
  }

  updateSigninStatus = ()=> {
    this.setSigninStatus();
  }

  setSigninStatus= async ()=>{
    console.log(this.state.googleAuth.currentUser.get())
    var user = this.state.googleAuth.currentUser.get();
    console.log(user)
    if (user.wc == null){
      this.setState({
        name: ''
      });
    }
    else{
      var isAuthorized = user.hasGrantedScopes(SCOPE);
      if(isAuthorized){
        console.log('USER')
        console.log(user)
        this.setState({
          name: user.vt.Ad
        });

        const boundary = '-------314159265358979323846';
        const delimiter = "\r\n--" + boundary + "\r\n";
        const close_delim = "\r\n--" + boundary + "--";
        var fileName='mychat123.png';
        var contentType='image/png'
        var metadata = {
          'name': fileName,
          'mimeType': contentType
        };

          var multipartRequestBody =
            delimiter +  'Content-Type: application/json\r\n\r\n' +
            JSON.stringify(metadata) +
            delimiter +
            'Content-Type: ' + contentType + '\r\n';

            multipartRequestBody +=  + '\r\n' + this.state.body;
            multipartRequestBody += close_delim;

          console.log(multipartRequestBody);
          var request = window.gapi.client.request({
            'path': 'https://www.googleapis.com/upload/drive/v3/files',
            'method': 'POST',
            'params': {'uploadType': 'multipart'},
            'headers': {
              'Content-Type': contentType
            },
            'body': multipartRequestBody
          });

        request.execute(function(file) {
          console.log(file)
        });
      }
    }
  }

  getBase64(file) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => resolve(reader.result);
      reader.onerror = error => reject(error);
    });
  }
  

  handleChange = async file => {
    let image

    if (file.currentTarget) {
      image = file.currentTarget.currentSrc;
      console.log(file.currentTarget)
    } else {
      if (!file.file.url && !file.file.preview) {
        file.file.preview = await this.getBase64(file.file.originFileObj);
      }
      image = file.file.preview;
      console.log(file.file)
      this.setState({
        body: file.file.preview
      });
    }

    console.log(image)
    
  }

  handleClientLoad = ()=>{
    window.gapi.load('client:auth2', this.initClient);
  }

  render() {
    return (
      <div className="App">
        <Upload 
          style={{width: '100%', height: '200px' }} 
          listType="picture-card" 
          onChange={this.handleChange} >
          <div>
            <div style={{ marginTop: 8 }}>Subir imagen</div>
          </div>
        </Upload>
        <div>UserName: <strong>{ this.state.name}</strong></div>
        <button id="signin-btn">Sign In</button>
        <button id="signout-btn">Sign Out</button>
      </div>
    );
  }
}

export default App;

为什么我的文件损坏了?它说这不是 PNG 文件。将图像上传到驱动器的正确方法是什么?谢谢!!

2个回答

改装要点:

  • 从您的脚本来看,这似乎this.state.body是 base64 数据。在这种情况下,需要添加Content-Transfer-Encoding: base64到请求正文中的数据头中。
    • 并且请注意请求正文的换行符multipart/form-data
  • 当您想使用时uploadType=multipart,请将multipart/form-data; boundary=###标题设置为内容类型。在您的脚本中,内容类型似乎是image/png.

当以上几点反映到你的脚本中时,它变成如下。

修改后的脚本:

从:
const boundary = '-------314159265358979323846';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
var fileName='mychat123.png';
var contentType='image/png'
var metadata = {
  'name': fileName,
  'mimeType': contentType
};

  var multipartRequestBody =
    delimiter +  'Content-Type: application/json\r\n\r\n' +
    JSON.stringify(metadata) +
    delimiter +
    'Content-Type: ' + contentType + '\r\n';

    multipartRequestBody +=  + '\r\n' + this.state.body;
    multipartRequestBody += close_delim;

  console.log(multipartRequestBody);
  var request = window.gapi.client.request({
    'path': 'https://www.googleapis.com/upload/drive/v3/files',
    'method': 'POST',
    'params': {'uploadType': 'multipart'},
    'headers': {
      'Content-Type': contentType
    },
    'body': multipartRequestBody
  });
到:
const boundary = '-------314159265358979323846';
const delimiter = "--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
var fileName = 'mychat123.png';
var contentType = 'image/png';
var metadata = {'name': fileName,'mimeType': contentType};
var multipartRequestBody = delimiter +
'Content-Type: application/json\r\n\r\n' +
JSON.stringify(metadata) + "\r\n" +
delimiter +
'Content-Type: ' + contentType + '\r\n' +
'Content-Transfer-Encoding: base64\r\n\r\n' +
this.state.body +
close_delim;
console.log(multipartRequestBody);
var request = window.gapi.client.request({
  'path': 'https://www.googleapis.com/upload/drive/v3/files',
  'method': 'POST',
  'params': {'uploadType': 'multipart'},
  'headers': {'Content-Type': 'multipart/form-data; boundary=' + boundary},
  'body': multipartRequestBody
});
  • 在Drive API的上传中,现阶段好像multipart/form-datamultipart/related都可以用。

笔记:

  • 在这个答案中,它假设您的访问令牌可用于将文件上传到 Google Drive。请注意这一点。

参考:

添加:

从您的回复中,我注意到您想使用fetch而不是gapi.client.request. 在这种情况下,示例脚本如下。

这是您评论中的脚本。

const fd = new FormData();
fd.append("file", this.state.body);
fd.append("title", 'test.png');
const options = {
  method: 'POST',
  headers: { Authorization: "Bearer" + " " + window.gapi.auth.getToken().access_token },
  body: fd
};
await fetch("googleapis.com/upload/drive/v3/files", options)
.then(response => response.json())
.then(jsonResp => { console.log(jsonResp) });

改装要点:

  • 在 Drive API v3 中,用于设置文件名的属性是name.
  • uploadType=multipart 需要在端点中使用。
  • 需要将 base64 数据转换为 blob。

当以上几点反映到你的脚本中时,它变成如下。

示例脚本:

// Reference: https://stackoverflow.com/a/16245768
// This method converts from base64 data to blob.
const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
  const byteCharacters = atob(b64Data);
  const byteArrays = [];

  for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
    const slice = byteCharacters.slice(offset, offset + sliceSize);

    const byteNumbers = new Array(slice.length);
    for (let i = 0; i < slice.length; i++) {
      byteNumbers[i] = slice.charCodeAt(i);
    }

    const byteArray = new Uint8Array(byteNumbers);
    byteArrays.push(byteArray);
  }

  const blob = new Blob(byteArrays, {type: contentType});
  return blob;
}

const metadata = {name: 'test.png'};
const fd = new FormData();
fd.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));
fd.append('file', b64toBlob(this.state.body, "image/png"));
fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart', {
  method: 'POST',
  headers: {Authorization: 'Bearer ' + window.gapi.auth.getToken().access_token},
  body: fd
})
.then(response => response.json())
.then(jsonResp => { console.log(jsonResp) });

我在react-uploady之上创建了一个自定义,称为drive-uploady,它使上传到 google drive 变得轻而易举,具有 Uploady 的所有功能:钩子、UI 组件、进度等。

例如:

import React from "react";
import DriveUploady from "drive-uploady";
import UploadButton from "@rpldy/upload-button";

export const App = () => {

    return <DriveUploady        
            clientId="<my-client-id>"
            scope="https://www.googleapis.com/auth/drive.file"
           >                
            <UploadButton>Upload to Drive</UploadButton>
        </DriveUploady>;
};

您只需要呈现一个上传按钮,让用户登录到 Drive 并上传。