使用 Google 选择器 API 后尝试下载文件时出现错误 401

Error 401 when trying to download a file after using Google Picker API

我有一个 React 应用程序,它允许用户输入特定的 Google 文档,然后我们分析其中的内容。对于第 1 步,选择文档,我使用 this nifty npm package,这对我来说也是一个“OAuth 令牌”,我认为它是一个 access_token,我可以用它来制作 API来电。我将返回的令牌和 Picker API 返回的数据设置为组件中的状态变量,如下所示

<GooglePicker clientId={process.env.REACT_APP_GOOGLE_CLIENT_ID}
  developerKey={process.env.REACT_APP_GOOGLE_API_KEY}
  scope={SCOPES}
  onAuthenticate={(token: any) => {
    setGoogleToken(token)
  }}
  onChange={(data: any) => {
    setGoogleData(data);
    nextOnboardingStage();
  }}
  onAuthFailed={(data: any) => console.log('on auth failed:', data)}
  multiselect={true}
  navHidden={true}
  authImmediate={false}
  mimeTypes={['application/vnd.google-apps.document']}
  viewId={'DOCS'}>
  <MyCustomButton />
</GooglePicker>

然后我尝试调用 Drive API export function 以在此函数中下载所述文件

const downloadGoogleDoc = async (authToken: string, fileID: string) => {
  const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export`, {
    headers: {
      'Authorization': `token ${authToken}`
    }
  });
  console.log(res);
};

但是,我在执行此操作时遇到了 401 错误。有人可以指出我在哪里失去了授权吗?非常感谢


编辑我的整个组件在下面

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

const Project: React.FC<ProjectInput> = () => {
  const [googleToken, setGoogleToken] = useState<string>();
  const [googleData, setGoogleData] = useState<any>({});

  const [documentData, setDocumentData] = useState();

  const downloadGoogleDoc = async (authToken: string, fileID: string) => {
    try {
      const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export`, {
        headers: {
          'Authorization': `Bearer ${authToken}`
        }
      });
      console.log(res);
    } catch(e) {
      console.log(e);
      console.log(e.message);
    }
  };

  console.log(googleToken);
  console.log(googleData);

  const MyCustomButton = () => {
    return (
      <button className="mx-8 my-2 p-2 rounded bg-blue-500 text-white">
        Select From Google Drive
      </button>
    )
  }

  if (googleToken && googleData.docs) {
    downloadGoogleDoc(googleToken, googleData.docs[0].id);
  }

  return (
    <div className={`ml-${sideBarWidth}`}>
        <FadeIn>
          <div className="flex flex-col min-h-screen bg-gray-500 py-6 justify-center sm:py-12">
            <div className="py-3 sm:max-w-xl sm:mx-auto">
              <div className="bg-white min-w-1xl flex flex-col rounded-xl shadow-lg">
                <div className="flex">
                  <div>
                    <div>
                      <div className="px-8 py-5">
                        <h2 className="text-gray-800 text-3xl font-semibold">Great! Select the document you'd like us to scan</h2>
                      </div>
                    </div>
                    <GooglePicker clientId={process.env.REACT_APP_GOOGLE_CLIENT_ID}
                      developerKey={process.env.REACT_APP_GOOGLE_API_KEY}
                      scope={SCOPES}
                      onAuthenticate={(token: any) => {
                        setGoogleToken(token)
                      }}
                      onChange={(data: any) => {
                        setGoogleData(data);
                        nextOnboardingStage();
                      }}
                      onAuthFailed={(data: any) => console.log('on auth failed:', data)}
                      multiselect={true}
                      navHidden={true}
                      authImmediate={false}
                      mimeTypes={['application/vnd.google-apps.document']}
                      viewId={'DOCS'}>
                      <MyCustomButton />
                    </GooglePicker>
                  </div>
              </div>
            </div>
          </div>
        </div>
      </FadeIn>
    </div>
  );
};

export default Project;

编辑

正如@Tanaike 所说,我需要在我的请求中包含 MimeType,工作函数如下

  const downloadGoogleDoc = async (authToken: string, fileID: string) => {
    try {
      const params = new URLSearchParams([['mimeType', 'text/html']]);
      const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export`, {
        params,
        headers: {
          'Authorization': `Bearer ${authToken}`
        }
      });
      console.log(res);
    } catch(e) {
      console.log(e);
      console.log(e.message);
    }
  };

我相信你的目标和你的情况如下。

  • 您想知道401错误的原因。
  • 根据您的脚本,我认为您可能想要下载 Google 文档文件(文档、电子表格、幻灯片等)以外的文件。
    • 因为下载Google文档文件时,端点不正确。在Drive API v3中使用“Files: export”方法时,需要使用mimeType导出。
    • 但是从你的回复中,我了解到你想使用“文件:导出”的方法下载一个 Google Docs 文件。
  • 您的访问令牌可用于从 Google 驱动器下载文件。

修改点:

  • 当我看到你下面的脚本时,我注意到除了端点之外的一个修改点。

      const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export`, {
        headers: {
          'Authorization': `token ${authToken}`
        }
      });
    
    • 在这种情况下,token是不正确的。请修改为Bearer。我认为这是错误 401.
    • 的原因
  • 当使用catch时,可以检索错误信息。

  • 如果下载二进制文件,则直接检索二进制数据。那时,当使用responseType: 'arrayBuffer'responseType: 'blob'时,数据分别被检索为array buffer和blob。

当以上几点反映到你的脚本中,就会变成下面这样。

修改后的脚本:

从:
const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export`, {
  headers: {
    'Authorization': `token ${authToken}`
  }
});
到:
const mimeType = "###"; // Please set the mimeType.
const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export?mimeType=${mimeType}`, {
  // responseType: `arrayBuffer`, // or 'blob'
  headers: {
    'Authorization': `Bearer ${authToken}`
  }
}).catch(err => console.log(err.response));
console.log(res);  // or res.data

注:

  • 在此修改后的脚本中,假设您的访问令牌可用于从 Google 驱动器下载文件。所以请注意这一点。

  • 当您使用“文件:导出”方式时,请使用Google Docs文件作为文件ID。请注意这一点。 Ref

参考文献: