如何从 GooglePicker 上的选定文件中获取 blob 您需要 Drive API参考

问题描述

我在 React 中使用 GooglePicker,得到的结果是一个对象数组...

[
  {
    "id": "1...m","serviceId": "docs","mimeType": "image/jpeg","name": "name.jpg","description": "","type": "photo","lastEditedUtc": 1575388407136,"iconUrl": "https://drive-thirdparty.googleusercontent.com/16/type/image/jpeg","url": "https://drive.google.com/file/d/1...m/view?usp=drive_web","embedUrl": "https://drive.google.com/file/d/1...m/preview?usp=drive_web","sizeBytes": 111364,"rotation": 0,"rotationDegree": 0,"parentId": "0...A"
}]

所以我尝试使用

通过https://www.googleapis.com/drive/v3/files和直接通过file.url访问

const fetchOptions = { headers: { Authorization: `Bearer ${accesstoken}` } };

docs.forEach((file) => {
  ...
  fetch(file.url,fetchOptions).then((res) => {
    const blob = res.blob();
    uploadFile(blob);
  });
});

但我得到 403CORS;我尝试在选择器中设置 relayUrl,但这破坏了选择器。

注意事项:

  1. 我的 auth2 中有这 3 个范围:
        ['https://www.googleapis.com/auth/drive.file','https://www.googleapis.com/auth/drive','https://www.googleapis.com/auth/drive.readonly']```
    
  2. 我将计算机的 url 端口和协议设置为 Authorized JavaScript origins 和 Authorized redirect URIs

有什么想法吗?


编辑 1:

我也尝试过像这样使用 Google Api

const FILE_URL = 'https://www.googleapis.com/drive/v3/files';
const url = isDoc
        ? `${FILE_URL}/${file.id}/export?mimeType=${mimeType}`
        : `${FILE_URL}/${file.id}?alt=media`;

      fetch(url,fetchOptions).then((res) => {
        const blob = res.blob();
        uploadFile(blob);
      });

解决方法

您需要 Drive API

从您的问题来看,您似乎正在尝试使用 Google Picker 完成所有工作。但是,选择器只会为您提供有限的文件元数据,因此您可以使用您的帐户打开它们(即在另一个窗口中查看它们)或让您上传文件。如果要下载实际文件,则需要使用 Drive API。

Drive Quickstart for browser JavaScript

流程可能是:

  • 让用户选择文件
  • 获取元数据对象
  • 从对象中提取文件 id
  • 调用 Drive API(getalt='media'

如果我误解了并且您已经在使用 Drive API,那么查看相关代码会很有帮助。

参考

编辑:

以下是使用 Picker API 通过 gapi 提供给 Drive API 的示例,使用相同的登录客户端。

HTML

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf-8" />
    <title>Google Picker Example</title>
    
  </head>
  <body>
    <button id="authorize_button" style="display: none;">Authorize</button>
    <button id="signout_button" style="display: none;">Sign Out</button>
    <div id="result"></div>

    <script type="text/javascript" src="script.js"></script>
    <script async defer src="https://apis.google.com/js/api.js"
    onload="this.onload=function(){};handleClientLoad()"
    onreadystatechange="if (this.readyState === 'complete') this.onload()">
  </script>
  </body>
</html>

JS

const API_KEY = 'AI...';
const CLIENT_ID = '44...';
const appId = "44...";

const SCOPES = ["https://www.googleapis.com/auth/drive"];

const DISCOVERY_DOCS = [
  "https://www.googleapis.com/discovery/v1/apis/drive/v3/rest",];

const authorizeButton = document.getElementById("authorize_button");
const signoutButton = document.getElementById("signout_button");

// Use the Google API Loader script to load the google.picker script.
function handleClientLoad() {
  gapi.load("client:auth2:picker",initClient);
}

function initClient() {
  gapi.client.init({
      apiKey: API_KEY,clientId: CLIENT_ID,discoveryDocs: DISCOVERY_DOCS,scope: SCOPES[0]
    })
    .then(
      function () {
        // Listen for sign-in state changes.
        gapi.auth2.getAuthInstance().isSignedIn.listen(handleSignIn);

        // Handle the initial sign-in state.
        handleSignIn(gapi.auth2.getAuthInstance().isSignedIn.get());
        authorizeButton.onclick = handleAuthClick;
        signoutButton.onclick = handleSignoutClick;
      },function (error) {
        appendPre(JSON.stringify(error,null,2));
      }
    );
}

function handleSignIn(isSignedIn) {
  if (isSignedIn) {
    authorizeButton.style.display = "none";
    signoutButton.style.display = "block";
    createPicker();
  } else {
    authorizeButton.style.display = "block";
    signoutButton.style.display = "none";
  }
}

function handleAuthClick(event) {
  gapi.auth2.getAuthInstance().signIn();
}

function handleSignoutClick(event) {
  gapi.auth2.getAuthInstance().signOut();
}

function createPicker() {
  const token = gapi.client.getToken().access_token
  if (token) {
    
    let view = new google.picker.View(google.picker.ViewId.DOCS);
    view.setMimeTypes("image/png,image/jpeg,image/jpg");
    let picker = new google.picker.PickerBuilder()
      .enableFeature(google.picker.Feature.NAV_HIDDEN)
      .enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
      .setAppId(appId)
      .setOAuthToken(token)
      .addView(view)
      .addView(new google.picker.DocsUploadView())
      .setDeveloperKey(API_KEY)
      .setCallback(getFile)
      .build();
    picker.setVisible(true);
  }
}

function getFile(pickerResp) {
  gapi.client.drive.files
    .get({
      fileId: pickerResp.docs[0].id,alt: 'media'
    })
    .then(resp => {
      console.log("fetch response",resp.status)
      let binary = resp.body
      // EDIT - addition from Gabrielle vvvv
      let l = binary.length
      let array = new Uint8Array(l);
      for (var i = 0; i<l; i++){
        array[i] = binary,charCodeAt(i);
      }
      let blob = new Blob([array],{type: 'application/octet-stream'});
      // EDIT - addition from Gabrielle ^^^^
}

此代码改编自 Drive QuickstartPicker Quickstart

注意 - 这确实会在控制台中出现错误,但它似乎完全一样。这似乎是 Picker 的错误 - https://issuetracker.google.com/177046274

来自 Gabrielle 的编辑
注意 - 使用带有 alt = media 的 get 是针对二进制文件的。要获取工作表/文档/幻灯片等,您需要使用 export 端点。