我可以读取使用 Google Picker 和 drive.file oauth 范围打开的驱动器文件吗?

Can I read a drive file opened using Google Picker with drive.file oauth scope?

我创建了一个表单插件,范围为:

https://www.googleapis.com/auth/drive.file

并创建了一个选择器:

function onOpen(e)
{
  FormApp.getUi().createAddonMenu()
  .addItem('Sync to Drive', 'showPicker')
  .addToUi();
}

function getOAuthToken() {
  return ScriptApp.getOAuthToken();
}

function showPicker() {
  var html = HtmlService.createHtmlOutputFromFile('Picker.html')
    .setWidth(600)
    .setHeight(425)
    .setSandboxMode(HtmlService.SandboxMode.IFRAME);
  FormApp.getUi().showModalDialog(html, 'Select Folder');
}

// Use Advanced Drive Service - https://developers.google.com/apps-script/advanced/drive
function getFileWithAdvancedDriveService(fileId)
{
  var drivefl = Drive.Files.get(fileId);
  FormApp.getUi().alert('File: '+drivefl);
  return drivefl;
}

// Use Drive Service - https://developers.google.com/apps-script/reference/drive
function getFileWithDriveService(fileId)
{
  var drivefl = DriveApp.getFileById(fileId);
  FormApp.getUi().alert('File: '+drivefl);
  return drivefl;
}

使用此选择器打开文件后,我尝试在 pickerCallback 中读取它:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css" />
    <script type="text/javascript">
      var DIALOG_DIMENSIONS = {
        width: 600,
        height: 425,
      };
      var authToken;
      var pickerApiLoaded = false;

      function onApiLoad() {
        gapi.load('picker', {
          callback: function () {
            pickerApiLoaded = true;
          },
        });
        google.script.run.withSuccessHandler(createPicker).withFailureHandler(showError).getOAuthToken();
      }

      function createPicker(token) {
        authToken = token;
        if (pickerApiLoaded && authToken) {
          var docsView = new google.picker.DocsView()
            .setIncludeFolders(true);

          var picker = new google.picker.PickerBuilder()
            .addView(docsView)
            .enableFeature(google.picker.Feature.NAV_HIDDEN)
            .hideTitleBar()
            .setOAuthToken(authToken)
            .setCallback(pickerCallback)
            .setOrigin('https://docs.google.com')
            .build();

          picker.setVisible(true);
        } else {
          showError('Unable to load the file picker.');
        }
      }

      function pickerCallback(data, ctx) {
        var action = data[google.picker.Response.ACTION];
        if(action == google.picker.Action.PICKED) {
          var doc = data[google.picker.Response.DOCUMENTS][0];
          var id = doc[google.picker.Document.ID];

          // Option 1 - Advanced Drive Service in apps script
          return google.script.run.withSuccessHandler(showMessage).withFailureHandler(showError).getFileWithAdvancedDriveService(id);

          // Option 2 - Drive Service in apps script
          return google.script.run.withSuccessHandler(showMessage).withFailureHandler(showError).getFileWithDriveService(id);

          // Option 3 - Drive Service in client
          return gapi.load('client', function () {
              gapi.client.load('drive', 'v2', function () {
                  gapi.client.setToken({ access_token: authToken });
                  var file = gapi.client.drive.files.get({ 'fileId': id });
                  file.execute(function (resp) {
                    showMessage(resp);
                  });
              });
          });
        } else if(action == google.picker.Action.CANCEL) {
          google.script.host.close();
        }
      }

      function showMessage(message) {
        document.getElementById('result').innerHTML = '<div>Message:</div> ' + JSON.stringify(message);
      }

      function showError(message) {
        document.getElementById('result').innerHTML = `<div>Error:</div> <div style="color:red;">${message}</div>`;
      }
    </script>
  </head>

  <body>
    <div>
      <p id="result"></p>
    </div>
    <script type="text/javascript" src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
  </body>
</html>
  1. gapi.client.drive.files.get 失败并出现以下错误: code: 404, message: "File not found: 1d0cqiT3aipgjMfLPolzgWVrnsl4xPxUJ1_7pH3ONVzU"

  2. 我在服务器端(应用程序脚本)尝试了同样的方法,但得到了类似的错误:

    DriveApp.getFolderById(文件 ID)

returns:

You do not have permission to call DriveApp.getFolderById. Required permissions: (https://www.googleapis.com/auth/drive.readonly || https://www.googleapis.com/auth/drive)
  1. 与高级驱动相同api:

    Drive.Files.get(文件 ID)

returns:

GoogleJsonResponseException: API call to drive.files.get failed with error: File not found: 1d0cqiT3aipgjMfLPolzgWVrnsl4xPxUJ1_7pH3ONVzU

我是否需要 drive.readonly 范围来读取用户使用 Google 选择器打开的文件?

是的,因为 https://www.googleapis.com/auth/drive.file 范围只允许您:

View and manage Google Drive files and folders that you have opened or created with this app

https://www.googleapis.com/auth/drive.readonly

See and download all your Google Drive files

您可以在此处查看 scopes 的完整列表。

我发现了问题。如果我从 pickerCallback:

移动这个 gapi.load 代码,它就会工作
      return gapi.load('client', function () {
          gapi.client.load('drive', 'v2', function () {
              gapi.client.setToken({ access_token: authToken });
              var file = gapi.client.drive.files.get({ 'fileId': id });
              file.execute(function (resp) {
                showMessage(resp);
              });
          });
      });

到javascript 加载:

  function onGsiLoad()
  {
    return gapi.load('client', function () {
      return gapi.client.load('drive', 'v2', function () {
        gsiLoaded = true;
        maybeEnablePicker();
      });
    });
  }

并且在pickerCallback中只有gapi.client.drive.files.get:

      var file = gapi.client.drive.files.get({ 'fileId': fileId });
      file.execute(function (resp) {
        showMessage(resp);
      });

工作测试用例在这里:https://docs.google.com/forms/d/1h3FWKVpGbCApg1_2unD3L86QlOmh9CIwK-W1Q97UTYQ/edit?usp=sharing

越野车一号在这里: https://docs.google.com/forms/d/1jpEa-mp8ccCZhEGlgBN52AML2i1oHIShFpY8Oe3GC44/edit?usp=sharing