Dropbox API v2 загружает большие файлы, используя python

Я пытаюсь загрузить большой файл (~900 МБ) через Dropbox API v2, но получаю эту ошибку:

request.exceptions.ConnectionError: ('Соединение прервано.', ConnectionResetError(104, 'Соединение установлено по одноранговому соединению'))

Работает нормально с небольшими файлами.

В документации я обнаружил, что мне нужно открыть сеанс загрузки с помощью метода files_upload_session_start, но у меня есть ошибка в этой команде, и я не могу идти дальше с методами.._append.

Как я могу решить эту проблему? Там нет информации в документах. Я использую Python 3.5.1 и последний модуль dropbox, установленный с помощью pip.

Вот код, к которому я бегу:

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 ответа

Решение

Для больших файлов, подобных этому, вам нужно использовать сеансы загрузки. В противном случае вы столкнетесь с такими проблемами, как опубликованная ошибка.

При этом используется Dropbox Python SDK для загрузки файла в API Dropbox из локального файла, как указано в file_path на удаленный путь, как указано dest_path, Он также выбирает, использовать ли сеанс загрузки на основе размера файла:

f = open(file_path)
file_size = os.path.getsize(file_path)

CHUNK_SIZE = 4 * 1024 * 1024

if file_size <= CHUNK_SIZE:

    print dbx.files_upload(f, dest_path)

else:

    upload_session_start_result = dbx.files_upload_session_start(f.read(CHUNK_SIZE))
    cursor = dropbox.files.UploadSessionCursor(session_id=upload_session_start_result.session_id,
                                               offset=f.tell())
    commit = dropbox.files.CommitInfo(path=dest_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()

@Greg ответ может быть обновлен с помощью Dropbox Api v2 call:

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

Несмотря на то, что ответ @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)

Он откроет сеанс без отправки каких-либо данных, затем в цикле добавит данные и, когда данных больше не останется, завершит работу. Он сделает больше вызовов, чем ответ Грега (в обмен на более читаемый код).

В 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)
Другие вопросы по тегам