Создание временного сжатого файла

Мне нужно создать временный файл для отправки, я пробовал:

# Create a temporary file --> I think it is ok (file not seen)
temporaryfile = NamedTemporaryFile(delete=False, dir=COMPRESSED_ROOT)

# The path to archive --> It ok
root_dir = "something"

# Create a compressed file --> It bugs
data = open(f.write(make_archive(f.name, 'zip', root_dir))).read()

# Send the file --> Its ok
response = HttpResponse(data, mimetype='application/zip')
response['Content-Disposition'] = 'attachment; filename="%s"' % unicode(downloadedassignment.name + '.zip')
return response

Я не знаю вообще, если это хороший подход.

Ответ 1

Прежде всего, вам не нужно создавать NamedTemporaryFile для использования make_archive; все, что вы хотите, это уникальное имя файла для создаваемого файла make_archive.

.write не возвращает имя файла

Чтобы сфокусироваться на этой ошибке: вы считаете, что возвращаемое значение f.write - это имя файла, которое вы можете открыть; просто попробуйте начать свой файл и прочитайте вместо этого:

f.write(make_archive(f.name, 'zip', root_dir))
f.seek(0)
data = f.read()

Обратите внимание, что вам также потребуется очистить созданный временный файл (вы установили delete=False):

import os
f.close()
os.unlink(f.name)

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

Это только что написал имя файла архива в новый файл.

Вы просто записываете новое имя архива в свой временный файл. Вам будет лучше просто прочитать архив напрямую:

data = open(make_archive(f.name, 'zip', root_dir), 'rb').read()

Обратите внимание, что теперь ваш временный файл вообще не записывается.

Лучший способ сделать это

Избегайте создания NamedTemporaryFile в целом: используйте tempfile.mkdtemp() вместо этого, чтобы создать временный каталог для размещения вашего архива, затем очистить после чего:

tmpdir = tempfile.mkdtemp()
try:
    tmparchive = os.path.join(tmpdir, 'archive')

    root_dir = "something"

    data = open(make_archive(tmparchive, 'zip', root_dir), 'rb').read()

finally:
    shutil.rmtree(tmpdir)

Ответ 2

Мне просто нужно было сделать что-то подобное, и я хотел бы, если это было возможно, полностью избежать ввода-вывода файлов. Вот что я придумал:

import tempfile
import zipfile

with tempfile.SpooledTemporaryFile() as tmp:
    with zipfile.ZipFile(tmp, 'w', zipfile.ZIP_DEFLATED) as archive:
        archive.writestr('something.txt', 'Some Content Here')

    # Reset file pointer
    tmp.seek(0)

    # Write file data to response
    return HttpResponse(tmp.read(), mimetype='application/x-zip-compressed')

Он использует SpooledTemporaryFile, поэтому он останется в памяти, если он не превышает пределы памяти. Затем я устанавливаю этот файл tempory как поток для ZipFile для использования. Имя файла, переданное в writestr, - это просто имя файла, которое файл будет иметь внутри архива, он не имеет ничего общего с файловой системой сервера. Затем мне просто нужно перемотать указатель на файл (seek(0)) после того, как ZipFile выполнил свою задачу и сбросил его на ответ.