简体   繁体   English

Google Client API v3 - 使用 Python 更新驱动器上的文件

[英]Google Client API v3 - update a file on drive using Python

I'm trying to update the content of a file from a python script using the google client api.我正在尝试使用 google 客户端 api 从 python 脚本更新文件的内容。 The problem is that I keep receiving error 403:问题是我不断收到错误 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.

I have tried to remove metadata fields, but didn't help.我试图删除元数据字段,但没有帮助。

The function to update the file is the following:更新文件的函数如下:

# 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

And here there is the whole script to reproduce the problem.这里有重现问题的整个脚本。 The goal is to substitute a file, retrieving its id by name.目标是替换一个文件,通过名称检索它的 id。 If the file does not exist yet, the script will create it by calling insert_file (this function works as expected).如果该文件尚不存在,脚本将通过调用insert_file创建它(此函数按预期工作)。 The problem is update_file , posted above.问题是上面发布的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()

In order to try the example, is necessary to create a Google Drive API from https://console.developers.google.com/apis/dashboard , then save the file credentials.js and pass its path to get_authenticated() .为了尝试该示例,有必要从https://console.developers.google.com/apis/dashboard创建一个 Google Drive API,然后保存文件credentials.js并将其路径传递给get_authenticated() The file token.json will be created after the first authentication and API authorization.文件token.json将在第一次身份验证和API 授权后创建。

The problem is that the metadata 'id' can not be changed when updating a file, so it should not be in the body.问题是元数据“id”在更新文件时不能更改,所以它不应该在正文中。 Just delete it from the 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

I tried your code with this modification and it works我用这个修改试过你的代码并且它有效

I also struggled a little bit with the function and found if you don't have to update the metadata then just remove them in the update function like: updated_file = service.files().update(fileId=file_id, media_body=media_body).execute()我也在该函数上遇到了一些问题,发现如果您不必更新元数据,那么只需在更新函数中删除它们,例如: updated_file = service.files().update(fileId=file_id, media_body=media_body).execute()

At Least that worked for me至少那对我有用

The problem is The resource body includes fields which are not directly writable.问题是The resource body includes fields which are not directly writable. So try removing all of the metadata properties and then add them back one by one.因此,请尝试删除所有元数据属性,然后将它们一一添加回来。 The one I would be suspicious about is trashed .我会怀疑的那个被trashed了。 Even though the API docs say this is writable, it shouldn't be.尽管 API 文档说这是可写的,但它不应该是。 Trashing a file has side effects beyond setting a boolean.删除文件除了设置布尔值之外还有副作用。 Updating a file and setting it to trashed at the same time is somewhat unusual.更新文件并同时将其设置为已trashed有点不寻常。 Are you sure that's what you intend?你确定那是你想要的吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM