Google Client API v3 - обновите файл на диске с помощью Python - PullRequest
0 голосов
/ 18 декабря 2018

Я пытаюсь обновить содержимое файла из скрипта Python с помощью API клиента Google.Проблема в том, что я получаю сообщение об ошибке 403:

An error occurred: <HttpError 403 when requesting https://www.googleapis.com /upload/drive/v3/files/...?alt=json&uploadType=resumable returned "The resource body includes fields which are not directly writable.

Я пытался удалить поля метаданных, но это не помогло.

Функция обновления файла следующая:

# File: utilities.py
from googleapiclient import errors
from googleapiclient.http import MediaFileUpload
from googleapiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools

def update_file(service, file_id, new_name, new_description, new_mime_type,
            new_filename):
"""Update an existing file's metadata and content.

Args:
    service: Drive API service instance.
    file_id: ID of the file to update.
    new_name: New name for the file.
    new_description: New description for the file.
    new_mime_type: New MIME type for the file.
    new_filename: Filename of the new content to upload.
    new_revision: Whether or not to create a new revision for this file.
Returns:
    Updated file metadata if successful, None otherwise.
"""
    try:
        # First retrieve the file from the API.
        file = service.files().get(fileId=file_id).execute()

        # File's new metadata.
        file['name'] = new_name
        file['description'] = new_description
        file['mimeType'] = new_mime_type
        file['trashed'] = True

        # File's new content.
        media_body = MediaFileUpload(
            new_filename, mimetype=new_mime_type, resumable=True)

        # Send the request to the API.
        updated_file = service.files().update(
            fileId=file_id,
            body=file,
            media_body=media_body).execute()
        return updated_file
    except errors.HttpError as error:
        print('An error occurred: %s' % error)
        return None

А вот и весь сценарий для воспроизведения проблемы.Цель состоит в том, чтобы заменить файл, извлекая его идентификатор по имени.Если файл еще не существует, скрипт создаст его, вызвав insert_file (эта функция работает как положено).Проблема в update_file, опубликованном выше.

from __future__ import print_function
from utilities import *
from googleapiclient import errors
from googleapiclient.http import MediaFileUpload
from googleapiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools

def get_authenticated(SCOPES, credential_file='credentials.json',
                  token_file='token.json', service_name='drive',
                  api_version='v3'):
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    store = file.Storage(token_file)
    creds = store.get()
    if not creds or creds.invalid:
        flow = client.flow_from_clientsecrets(credential_file, SCOPES)
        creds = tools.run_flow(flow, store)
    service = build(service_name, api_version, http=creds.authorize(Http()))
    return service


def retrieve_all_files(service):
    """Retrieve a list of File resources.

    Args:
        service: Drive API service instance.
    Returns:
        List of File resources.
    """

    result = []
    page_token = None
    while True:
        try:
            param = {}
            if page_token:
                param['pageToken'] = page_token
            files = service.files().list(**param).execute()

            result.extend(files['files'])
            page_token = files.get('nextPageToken')
            if not page_token:
                break
        except errors.HttpError as error:
            print('An error occurred: %s' % error)
            break

    return result


def insert_file(service, name, description, parent_id, mime_type, filename):
    """Insert new file.

    Args:
        service: Drive API service instance.
        name: Name of the file to insert, including the extension.
        description: Description of the file to insert.
        parent_id: Parent folder's ID.
        mime_type: MIME type of the file to insert.
        filename: Filename of the file to insert.
    Returns:
        Inserted file metadata if successful, None otherwise.
    """
    media_body = MediaFileUpload(filename, mimetype=mime_type, resumable=True)
    body = {
        'name': name,
        'description': description,
        'mimeType': mime_type
    }
    # Set the parent folder.
    if parent_id:
        body['parents'] = [{'id': parent_id}]

    try:
        file = service.files().create(
            body=body,
            media_body=media_body).execute()

        # Uncomment the following line to print the File ID
        # print 'File ID: %s' % file['id']

        return file
    except errors.HttpError as error:
        print('An error occurred: %s' % error)
        return None


# If modifying these scopes, delete the file token.json.
SCOPES = 'https://www.googleapis.com/auth/drive'

def main():
    service = get_authenticated(SCOPES)

    # Call the Drive v3 API
    results = retrieve_all_files(service)

    target_file_descr = 'Description of deploy.py'
    target_file = 'deploy.py'
    target_file_name = target_file
    target_file_id = [file['id'] for file in results if file['name'] == target_file_name]

    if len(target_file_id) == 0:
        print('No file called %s found in root. Create it:' % target_file_name)
        file_uploaded = insert_file(service, target_file_name, target_file_descr, None,
                                'text/x-script.phyton', target_file_name)
    else:
        print('File called %s found. Update it:' % target_file_name)
        file_uploaded = update_file(service, target_file_id[0], target_file_name, target_file_descr,
                                'text/x-script.phyton', target_file_name)

    print(str(file_uploaded))


if __name__ == '__main__':
    main()

Чтобы попробовать пример, необходимо создать Google Drive API из https://console.developers.google.com/apis/dashboard,, затем сохранить файл credentials.js и передатьего путь к get_authenticated().Файл token.json будет создан после первой аутентификации и авторизации API.

Ответы [ 2 ]

0 голосов
/ 11 июня 2019

Проблема в том, что метаданные 'id' нельзя изменить при обновлении файла, поэтому их не должно быть в теле.Просто удалите его из dict:

# File's new metadata.
del file['id']  # 'id' has to be deleted
file['name'] = new_name
file['description'] = new_description
file['mimeType'] = new_mime_type
file['trashed'] = True

Я попробовал ваш код с этой модификацией, и она работает

0 голосов
/ 18 декабря 2018

Проблема в The resource body includes fields which are not directly writable. Поэтому попробуйте удалить все свойства метаданных, а затем добавить их обратно по одному.Я бы с подозрением отнесся к trashed.Хотя в документации API говорится, что это доступно для записи, этого не должно быть.Удаление файла имеет побочные эффекты помимо установки логического значения.Обновление файла и установка его на trashed в то же время несколько необычно.Вы уверены, что это то, что вы намерены?

...