Экспорт GDrive с использованием кредитов сервисного аккаунта завершился неудачно с ошибкой 404
У меня есть сценарий для экспорта текста из файла GDrive с помощью клиента OAuth, который отлично работает -
import googleapiclient.discovery as google
from apiclient.http import MediaIoBaseDownload
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
import datetime, io, os, pickle
Scopes=" ".join(['https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.metadata',
'https://www.googleapis.com/auth/drive.readonly'])
TokenFile="token.pickle"
def init_creds(clientfile,
scopes,
tokenfile=TokenFile):
token=None
if os.path.exists(tokenfile):
with open(tokenfile, 'rb') as f:
token=pickle.load(f)
if (not token or
not token.valid or
token.expiry < datetime.datetime.utcnow()):
if (token and
token.expired and
token.refresh_token):
token.refresh(Request())
else:
flow=InstalledAppFlow.from_client_secrets_file(clientfile, scopes)
token=flow.run_local_server(port=0)
with open(tokenfile, 'wb') as f:
pickle.dump(token, f)
return token
def export_text(id,
clientfile,
scopes=Scopes):
creds=init_creds(clientfile=clientfile,
scopes=scopes)
service=google.build('drive', 'v3', credentials=creds)
request=service.files().export_media(fileId=id,
mimeType='text/plain')
buf=io.BytesIO()
downloader, done = MediaIoBaseDownload(buf, request), False
while done is False:
status, done = downloader.next_chunk()
destfilename="tmp/%s.txt" % id
return buf.getvalue().decode("utf-8")
if __name__=='__main__':
print (export_text(id="#{redacted}"
clientfile="/path/to/oath/client.json"))
Но мне больно каждый раз проходить поток OAuth, и, поскольку только я использую сценарий, я хочу упростить вещи и вместо этого использовать учетную запись службы, следуя этому сообщению -
Пример учетной записи службы Python Google Drive API
Мой новый скрипт учетной записи службы, делающий то же самое, выглядит следующим образом:
import googleapiclient.discovery as google
from oauth2client.service_account import ServiceAccountCredentials
from apiclient.http import MediaIoBaseDownload
import io
Scopes=" ".join(['https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.metadata',
'https://www.googleapis.com/auth/drive.readonly'])
def export_text(id,
clientfile,
scopes=Scopes):
creds=ServiceAccountCredentials.from_json_keyfile_name(clientfile,
scopes)
service=google.build('drive', 'v3', credentials=creds)
request=service.files().export_media(fileId=id,
mimeType='text/plain')
buf=io.BytesIO()
downloader, done = MediaIoBaseDownload(buf, request), False
while done is False:
status, done = downloader.next_chunk()
destfilename="tmp/%s.txt" % id
return buf.getvalue().decode("utf-8")
if __name__=='__main__':
print (export_text(id="#{redacted}",
clientfile="path/to/service/account.json"))
но когда я запускаю его для того же
id
, Я получаю следующее -
googleapiclient.errors.HttpError: <HttpError 404 when requesting https://www.googleapis.com/drive/v3/files/#{redacted}/export?mimeType=text%2Fplain&alt=media returned "File not found: #{redacted}.">
Похоже, что сценарий учетной записи службы проходит этап аутентификации (т.е. кредиты учетной записи службы в порядке), но затем не удается получить файл - странно, поскольку я могу получить его нормально, используя версию OAuth:/
Любые мысли о том, что может вызвать эту ошибку 404 в версии учетной записи службы, учитывая, что версия клиента OAuth явно работает для того же
id
?
TIA.
2 ответа
Ответ:
Вам необходимо поделиться своим файлом с учетной записью службы.
Больше информации:
Как и в случае с любым файлом, вам необходимо предоставить пользователю явные разрешения, чтобы иметь возможность его видеть. Поскольку учетная запись службы - это отдельное право для вас, это также относится и к ним.
Используя настройки общего доступа к файлам (вы можете просто сделать это в пользовательском интерфейсе Диска, щелкнув файл правой кнопкой мыши и выбрав
Share
), дайте адресу электронной почты учетной записи службы правильное разрешение (чтение / запись). Электронный адрес учетной записи службы имеет следующий вид:
service-account-name@project-id.iam.gserviceaccount.com
Надеюсь, это поможет вам!
Перед тем как позвонить, создайте File.list, чтобы увидеть, к каким файлам у учетной записи службы есть доступ. Выполнение file.get для файла, к которому у учетной записи службы нет доступа, приведет к ошибке "файл не найден". Помните, что учетная запись службы - это не вы, у нее есть собственная учетная запись на Google Диске. Любые файлы, к которым вы хотите получить доступ, должны быть загружены в его учетную запись или переданы учетной записи службы.
Если файл file.list не работает, это может предположить, что с авторизацией что-то не так, и вы должны убедиться, что учетная запись службы имеет доступ к клиентскому файлу, возможно, это тот файл, который он не может найти.
Предоставление доступа к сервисному аккаунту
Создайте каталог в своей личной учетной записи Google Drive. Возьмите адрес электронной почты учетной записи службы, он находится в загруженном вами ключевом файле, в нем есть @. Затем поделитесь этим каталогом в своей учетной записи на диске с учетной записью службы, как если бы вы делились с любым другим пользователем.
- Добавление файлов в этот каталог может предоставить или не предоставить учетной записи службы автоматический доступ к ним с разрешениями - это проблема, которая может потребоваться для совместного использования файла с учетной записью службы.
- Не забудьте, что сервисная учетная запись предоставит вашей личной учетной записи разрешения на файл, когда он загружает его, он будет владельцем.