Python - передача файла с HTTP (S) URL на FTP / Dropbox без записи на диск (загрузка по частям) - PullRequest
0 голосов
/ 29 ноября 2018

У меня есть большой файл (500 МБ-1 ГБ), хранящийся в папке HTTP (S)
(скажем, https://example.com/largefile.zip).

У меня есть доступ на чтение / запись к FTP-серверу

У меня есть обычные права пользователя (без sudo).

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

Так что обычно я делаю.

response=requests.get('https://example.com/largefile.zip', stream=True)
with open("largefile_local.zip", "wb") as handle:                                                                                                     
 for data in response.iter_content(chunk_size=4096):
  handle.write(data)     

и затем загружаю локальный файл на FTP.Но я хочу избежать дискового ввода-вывода.Я не могу смонтировать FTP как файловую систему fuse, потому что у меня нет прав суперпользователя.

В идеале я хотел бы сделать что-то вроде ftp_file.write() вместо handle.write().Это возможно?В документации ftplib предполагается, что будут загружены только локальные файлы, а не response.content.Так что в идеале я хотел бы сделать

response=requests.get('https://example.com/largefile.zip', stream=True)
for data in response.iter_content(chunk_size=4096):
 ftp_send_chunk(data)   

Я не уверен, как написать ftp_send_chunk().

Здесь есть похожий вопрос ( Python - Загрузить в памятифайл (сгенерированный вызовами API) в FTP чанками ).Мой вариант использования требует извлечения фрагмента из HTTP-URL и записи его на FTP.

PS: Решение, предоставленное в ответе (обертка вокруг urllib.urlopen), также будет работать с загрузками Dropbox.У меня были проблемы с работой с моим провайдером ftp, поэтому, наконец, я использовал dropbox, который работает надежно.

Обратите внимание, что Dropbox имеет функцию «добавить веб-загрузку» в API, которая делает то же самое (удаленная загрузка).Это работает только с "прямыми" ссылками.В моем случае использование http_url происходило от службы потоковой передачи, которая была ограничена IP.Таким образом, этот обходной путь стал необходимым.Вот код

import dropbox;
d = dropbox.Dropbox(<ACTION-TOKEN>);
f=FileWithProgress(filehandle);
filesize=filehandle.length;
targetfile='/'+fname;
CHUNK_SIZE=4*1024*1024
upload_session_start_result = d.files_upload_session_start(f.read(CHUNK_SIZE));
num_chunks=1
cursor = dropbox.files.UploadSessionCursor(session_id=upload_session_start_result.session_id,
                                           offset=CHUNK_SIZE*num_chunks)
commit = dropbox.files.CommitInfo(path=targetfile)
while CHUNK_SIZE*num_chunks < filesize:
 if ((filesize - (CHUNK_SIZE*num_chunks)) <= CHUNK_SIZE):
  print d.files_upload_session_finish(f.read(CHUNK_SIZE),cursor,commit)
 else:
  d.files_upload_session_append(f.read(CHUNK_SIZE),cursor.session_id,cursor.offset)
 num_chunks+=1
cursor.offset = CHUNK_SIZE*num_chunks
link = d.sharing_create_shared_link(targetfile)  
url = link.url
dl_url = re.sub(r"\?dl\=0", "?dl=1", url)
dl_url = dl_url.strip()
print 'dropbox_url: ',dl_url;

Я думаю, что это даже можно сделать с помощью google-drive через их python api, но использование учетных данных с их оболочкой python слишком сложно для меня.Проверьте это 1 и это 2

1 Ответ

0 голосов
/ 29 ноября 2018

Это должно быть просто с urllib.request.urlopen, так как он возвращает файл-подобный объект, который вы можете использовать напрямую с FTP.storbinary.

ftp = FTP(host, user, passwd)

filehandle = urllib.request.urlopen(http_url)

ftp.storbinary("STOR /ftp/path/file.dat", filehandle)

Если вы хотите отслеживать ход выполнения, внедрите файл-объект-оболочку, который будет делегировать вызовы filehandle объекту, но также будет отображать ход выполнения:

class FileWithProgress:

    def __init__(self, filehandle):
        self.filehandle = filehandle
        self.p = 0

    def read(self, blocksize):
        r = self.filehandle.read(blocksize)
        self.p += len(r)
        print(str(self.p) + " of " + str(self.p + self.filehandle.length)) 
        return r

filehandle = urllib.request.urlopen(http_url)

ftp.storbinary("STOR /ftp/path/file.dat", FileWithProgress(filehandle))

Для Python2 использовать:

  • urllib.urlopen вместо urllib.request.urlopen.
  • filehandle.info().getheader('Content-Length') вместо str(self.p + filehandle.length)
...