Ошибка «EOFError: конец потока уже достигнут» при попытке распаковать большой файл на лету с помощью Python и smart_open - PullRequest
0 голосов
/ 26 мая 2019

Я пытаюсь загрузить и распаковать набор файлов с удаленного сервера Apache. Я предоставляю список файлов .tbz (tar.bz2) для загрузки и распаковки на лету. Цель состоит в том, чтобы передать их с удаленного сервера Apache через распаковщик tar и немедленно направить их в мое хранилище Amazon AWS S3. Я делаю это, потому что файлы могут быть размером до 30 ГБ.

Я использую библиотеку python "smart_open", чтобы абстрагироваться от управления https и s3.

Код, который я здесь предоставил, прекрасно работает для небольших файлов. Как только я пытаюсь сделать это с большим файлом (более 8 МБ), я получаю следующую ошибку:

"EOFError: End of stream already reached"

Вот трассировка:

Traceback (most recent call last):
  File "./script.py", line 28, in <module>
    download_file(fileName)
  File "./script.py", line 21, in download_file
    for line in tfext:
  File "/.../lib/python3.7/tarfile.py", line 706, in readinto
    buf = self.read(len(b))
  File "/.../lib/python3.7/tarfile.py", line 695, in read
    b = self.fileobj.read(length)
  File "/.../lib/python3.7/tarfile.py", line 537, in read
    buf = self._read(size)
  File "/.../lib/python3.7/tarfile.py", line 554, in _read
    buf = self.cmp.decompress(buf)
EOFError: End of stream already reached

Когда я распечатываю строки, которые пишу в поток, я вижу, что я все еще прохожу первую часть файла до того, как выдается ошибка.

Что я пробовал до сих пор:

  1. Я попытался указать одинаковый размер буфера для open () и tarfile.open (), но безуспешно.

  2. Я также пытался ввести некоторую задержку между написанием каждой строки, но безрезультатно.

from smart_open import open
import tarfile

baseUrl = 'https://someurlpath/'
filesToDownload = ['name_of_file_to_download']

def download_file(fileName):
    fileUrl = baseUrl + fileName + '.tbz'
    with open(fileUrl, 'rb') as fin:
        with tarfile.open(fileobj=fin, mode='r|bz2') as tf:
            destination = 's3://some_aws_path/' + fileName + '.csv'
            with open(destination, 'wb') as fout:
                with tf.extractfile(tf.next()) as tfext:
                    for line in tfext:
                        fout.write(line)


for fileName in filesToDownload:
    download_file(fileName)

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

1 Ответ

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

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

from smart_open import open
import tarfile
import boto3
from codecs import open as copen

filenames = ['test.tar.bz2',]

def download_file(fileName):
   s3 = boto3.resource('s3')
   bucket = s3.Bucket('bucketname')
   obj = bucket.Object(fileName)
   local_filename = '/tmp/{}'.format(fileName)
   obj.download_file(local_filename)
   tf = tarfile.open(local_filename, 'r:bz2')
   for member in tf.getmembers():
      tf.extract(member)
      fd = open(member.name, 'rb')
      print(member, len(fd.read()))
if __name__ == '__main__':
   for f in filenames:
      download_file(f)
...