首页 > 解决方案 > 如何从 GooglePicker 上的选定文件中获取 blob

问题描述

我将 GooglePicker 与 React 一起使用,我得到的结果是一组对象......

[
  {
    "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);
      });

标签: google-apigoogle-api-js-clientgoogle-picker

解决方案


您需要 Drive API

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

用于浏览器 JavaScript 的云端硬盘快速入门

流程可能是:

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

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

参考

编辑:

这是一个使用 Picker APIgapi通过相同的登录客户端向 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
Note的编辑- 使用 get withalt = media用于二进制文件。要获取工作表/文档/幻灯片等,您需要使用export终点。


推荐阅读