Dropbox API v2使用Python上传大文件

8
我正在尝试通过Dropbox API v2上传大文件(约900MB),但出现以下错误:
requests.exceptions.ConnectionError:(“连接中止。”, ConnectionResetError(104,“对等方重置连接”))
小文件可以正常工作。
我在文档中发现需要使用files_upload_session_start方法打开上传会话,但是我在此命令上遇到了错误,并且无法使用._append方法继续操作。
如何解决这个问题?文档中没有信息。 我正在使用Python 3.5.1和使用pip安装的最新的dropbox模块。
这是我运行的代码:
c = Dropbox(access_token)
f = open("D:\\Programs\\ubuntu-13.10-desktop-amd64.iso", "rb")
result = c.files_upload_session_start(f)
f.seek(0, os.SEEK_END)
size = f.tell()
c.files_upload_session_finish(f,     files.UploadSessionCursor(result.session_id, size), files.CommitInfo("/test900.iso"))
3个回答

30

对于像这样的大文件,您需要使用上传会话。否则,您将遇到类似于您发布的错误的问题。

此代码使用Dropbox Python SDK从本地文件(由file_path指定)上传文件到Dropbox API,并将其保存到远程路径(由dest_path指定)。根据文件大小,它还将选择是否使用上传会话:

import os

from tqdm import tqdm

import dropbox


def upload(
    access_token,
    file_path,
    target_path,
    timeout=900,
    chunk_size=4 * 1024 * 1024,
):
    dbx = dropbox.Dropbox(access_token, timeout=timeout)
    with open(file_path, "rb") as f:
        file_size = os.path.getsize(file_path)
        if file_size <= chunk_size:
            print(dbx.files_upload(f.read(), target_path))
        else:
            with tqdm(total=file_size, desc="Uploaded") as pbar:
                upload_session_start_result = dbx.files_upload_session_start(
                    f.read(chunk_size)
                )
                pbar.update(chunk_size)
                cursor = dropbox.files.UploadSessionCursor(
                    session_id=upload_session_start_result.session_id,
                    offset=f.tell(),
                )
                commit = dropbox.files.CommitInfo(path=target_path)
                while f.tell() < file_size:
                    if (file_size - f.tell()) <= chunk_size:
                        print(
                            dbx.files_upload_session_finish(
                                f.read(chunk_size), cursor, commit
                            )
                        )
                    else:
                        dbx.files_upload_session_append(
                            f.read(chunk_size),
                            cursor.session_id,
                            cursor.offset,
                        )
                        cursor.offset = f.tell()
                    pbar.update(chunk_size)

2
如果在Python 3中遇到字节错误,我发现使用with open(file_path, 'rb') as f:而不是f = open(file_path)对我有用。使用with时不要忘记缩进。 - Kalob Taulien
2
值得注意的是,如果要上传较大的文件,您可能需要使用timeout参数初始化Dropbox类对象,例如:dbx = dropbox.Dropbox(DROPBOX_ACCESS_TOKEN, timeout=900)。当MySQL从库备份增长到约3GB后,有时会达到默认的30秒限制。 - synweap15

7

@Greg的回答可以使用Dropbox API v2调用进行更新:

self.client.files_upload_session_append_v2(
                f.read(self.CHUNK_SIZE), cursor)
cursor.offset = f.tell()

1

尽管@Greg的答案非常完整且是最好的解决方案(也是最有效的),但我想分享这个最小化实现,以便那些想快速学习的人:

def file_upload(dbx: dropbox.Dropbox, local_path: pathlib.Path, remote_path: str):
    CHUNKSIZE = 100 * 1024 * 1024
    upload_session_start_result = dbx.files_upload_session_start(b'')
    cursor = dropbox.files.UploadSessionCursor(
        session_id=upload_session_start_result.session_id,
        offset=0
    )
    with local_path.open("rb") as f:
        while True:
            data = f.read(CHUNKSIZE)
            if data == b"":
                break
            logger.debug("Pushing %d bytes", len(data))
            dbx.files_upload_session_append_v2(data, cursor)
            cursor.offset += len(data)
    commit = dropbox.files.CommitInfo(path=remote_path)
    dbx.files_upload_session_finish(b'', cursor, commit)

它将打开一个会话而不发送任何数据,然后在循环中添加数据,当没有更多的数据时,结束。它将比Greg的答案做更多的调用(以换取更可读的代码)。

在Python 3.8+中,您可以使用赋值表达式使此代码变得更加美观(我认为):

def file_upload(dbx: dropbox.Dropbox, local_path: pathlib.Path, remote_path: str):
    CHUNKSIZE = 1 * 1024
    upload_session_start_result = dbx.files_upload_session_start(b'')
    cursor = dropbox.files.UploadSessionCursor(
        session_id=upload_session_start_result.session_id,
        offset=0
    )
    with local_path.open("rb") as f:
        while (data := f.read(CHUNKSIZE)) != b"":
            logger.debug("Pushing %d bytes", len(data))
            dbx.files_upload_session_append_v2(data, cursor)
            cursor.offset += len(data)
    commit = dropbox.files.CommitInfo(path=remote_path)
    dbx.files_upload_session_finish(b'', cursor, commit)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接