Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
208 views
in Technique[技术] by (71.8m points)

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

I have a react app that allows user to pipe in a particular Google Doc that we then analyze the content of. For step 1, picking the document, I'm using this nifty npm package, which also returns to me an "OAuth token", which I presume to be an access_token I can use to make API calls. I set the returned token and the data returned by the Picker API as state variables in my component, as seen below

<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>

I then try to call the Drive API export function to download said file in this 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);
};

However, I'm getting a 401 error when doing this. Can someone point out where I'm losing the auth? Thanks so much


Edit My entire component is below

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;

EDIT

As @Tanaike said, I needed to include MimeType in my request, working function is below

  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);
    }
  };
question from:https://stackoverflow.com/questions/66057253/error-401-when-trying-to-download-a-file-after-using-google-picker-api

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I believe your goal and your situation as follows.

  • You want to know the reason of the error of 401.
  • From your script, I thought that you might want to download a file which is not Google Docs file (Document, Spreadsheet, Slides and so on).
    • Because when the Google Docs file is downloaded, the endpoint is not correct. When you use the method of "Files: export" in Drive API v3, mimeType for exporting is required to be used.
    • But from your replying, I understood that you wanted to download a Google Docs file using the method of "Files: export".
  • Your access token can be used for downloading the file from Google Drive.

Modification points:

  • When I saw your following script, I noticed a modification point except for endpoint.

      const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export`, {
        headers: {
          'Authorization': `token ${authToken}`
        }
      });
    
    • In this case, token is not correct. Please modify it to Bearer. I thought that this is the reason of the error 401.
  • When catch is used, the error message can be retrieved.

  • If you download a binary file, the binary data is directly retrieved. At that time, when responseType: 'arrayBuffer' and responseType: 'blob' are used, the data is retrieved as array buffer and blob, respectively.

When above points are reflected to your script, it becomes as follows.

Modified script:

From:
const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export`, {
  headers: {
    'Authorization': `token ${authToken}`
  }
});
To:
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

Note:

  • In this modified script, it supposes that your access token can be used for downloading the file from Google Drive. So please be careful this.

  • When you use the method of "Files: export", please use the Google Docs files as the file ID. Please be careful this. Ref

References:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...