我正在尝试使用REST API和服务帐户将媒体文件上传到Google Drive。我有一个云功能后端,在那里我使用Google DRIVE API的正确作用域进行身份验证,并将访问令牌(如下面的片段所示(返回给客户端,然后客户端可以向Google DRIVE发出上载请求。
const auth = new google.auth.GoogleAuth({
scopes: [
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive'
]
});
const client = await auth.getClient();
return await client.getAccessToken();
这在这里看到的网络上运行良好,甚至在这里看到了世博会小吃网站上。现在,我需要使用React Native在移动平台上使用它,但每次上传文件时,我都会得到200的响应和文件id,但我在指定的文件夹或谷歌硬盘上的其他任何地方都找不到该文件。下面的片段只是对我上传到网络版本时使用的相同逻辑的重写。
import React, {useEffect, useState} from 'react'
import {View, Text, StyleSheet, Platform, Button, Image} from 'react-native'
import * as ImagePicker from 'expo-image-picker';
import axios from 'axios'
import {decode as atob} from 'base-64'
export const UploadToDrive = (props) => {
const [image, setImage] = useState(null);
const [uploadPercent, setUploadPercent] = useState(0)
const [mimeType, setMimeType] = useState('')
useEffect(() => {
(async () => {
if (Platform.OS !== 'web') {
const { status } = await ImagePicker.requestCameraRollPermissionsAsync();
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions to make this work!');
}
}
})();
}, []);
const pickImage = async () => {
const baseURL = 'https://dummy-server-address/api';
// Simultaneously get the access token while picking the media
const [{response}, result] = await Promise.all([
fetch(
`${baseURL}/services/fetch-access-token`,
{
mode: "cors",
}
)
.then((res) => res.json())
.then((resp) => resp)
.catch((err) => console.log(err)),
ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
})
]).catch((err) => console.log(err))
// ensure that all needed parameters/fields are present
if (!result.cancelled && response.token) {
const accessToken = response.token
const uri = "file:///" + result.uri.split("file:/").join("");
setImage(uri);
const file = result
let filename = uri.split('/').pop();
let match = /.(w+)$/.exec(filename);
let type = match ? `image/${match[1]}` : `image`;
file.name = filename;
file.type = type
file.uri = uri
console.log(file)
setMimeType(file.type)
// Upload the image using the fetch and FormData APIs
let formData = new FormData();
const metadata = {
name: file.name,
type: file.type || 'multipart/form-data',
parents: ["1yz6MUU0YfXz0rl7TObq-JOPCmC6sHKdQ"],
};
// construct the file metadata for the upload
formData.append(
"metadata",
new Blob([JSON.stringify(metadata)], { type: "application/json" })
);
// formData.append('file', { uri, name: filename, type });
formData.append('file', file);
const url =
"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id";
// let uploadPercent;
const upload_res = await axios(url, {
method: "POST",
headers: {
Authorization: "Bearer " + accessToken,
},
data: formData,
onUploadProgress: (p) => {
setUploadPercent((p.loaded / p.total) * 100)
},
})
.catch(err => console.log({err}))
.finally(() => setUploadPercent(0));
console.log({ data: upload_res.data });
} else {
throw new Error('You do not have all the right variables to make an upload')
}
};
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
{!!uploadPercent && <Text style={styles.uploadPercent}>Upload Percent: {`${uploadPercent.toFixed(2)}%`}</Text>}
<Button style={styles.button} title="Pick a file" onPress={pickImage} />
{image && mimeType.includes('image') && <Image source={{ uri: image }} style={{ width: 200, height: 200, marginTop: 20 }} />}
</View>
)
}
const styles = StyleSheet.create({
button: {
position: 'absolute',
top: '10%'
},
uploadPercent: {
color: 'tomato',
fontSize: 15,
fontWeight: 'bold',
marginBottom: 20
}
})
拜托,我可能做错了什么?
-
如果使用服务帐户文件上传到非他的驱动器,则需要将参数
supportsAllDrives
设置为true
-
或者,使用域范围的委派和模拟,使服务帐户代表您上载文件-在这种情况下,您不需要与服务帐户共享文件夹。
请记住,服务帐户不是您。服务帐户有自己的Google驱动器帐户。如果你做一个files.list,你会看到这些文件。它们正在上传到服务帐户驱动器帐户。
如果你想把它们上传到你自己的谷歌驱动器帐户,那么你可以使用服务帐户的电子邮件地址,在你的个人驱动器帐户上共享一个文件夹,并授予它上传到该文件夹的权限。只需记住让服务帐户授予您的个人帐户访问文件的权限,否则您将无法访问这些文件,因为服务帐户将在上传文件时成为所有者。
如果你有一个gsuite帐户,那么你可以设置upl域范围的委派,并允许服务帐户模拟域上的用户并代表该用户上传,这意味着权限将不再有问题。
元数据名称问题
你的元数据应该包括你正在上传的文件的名称,如果你不这样做,那么它将以无标题为名称上传。您应该传递元数据变量,这似乎是在传递元数据字符串。
formData.append(
"metadata",
new Blob([JSON.stringify(metadata)], { type: "application/json" })
);