Отправка нескольких файлов.CSV в.ZIP без сохранения на диске в Python

Я работаю над отчетным приложением для моего сайта на Django. Я хочу запустить несколько отчетов, и каждый отчет должен генерировать файл.csv в памяти, который можно загрузить в пакетном режиме в виде.zip. Я хотел бы сделать это без сохранения каких-либо файлов на диск. Итак, чтобы сгенерировать один файл.csv, я выполняю обычную операцию:

mem_file = StringIO.StringIO()
writer = csv.writer(mem_file)
writer.writerow(["My content", my_value])
mem_file.seek(0)
response = HttpResponse(mem_file, content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename=my_file.csv'

Это прекрасно работает, но только для одного, разархивированного.csv. Если бы у меня был, например, список файлов.csv, созданных с помощью потока StringIO:

firstFile = StringIO.StringIO()
# write some data to the file

secondFile = StringIO.StringIO()
# write some data to the file

thirdFile = StringIO.StringIO()
# write some data to the file

myFiles = [firstFile, secondFile, thirdFile]

Как я могу вернуть сжатый файл, который содержит все объекты в myFiles и может быть правильно разархивирован, чтобы показать три файла.csv?

3 ответа

Решение

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

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

import zipfile
import StringIO

zipped_file = StringIO.StringIO()
with zipfile.ZipFile(zipped_file, 'w') as zip:
    for i, file in enumerate(files):
        file.seek(0)
        zip.writestr("{}.csv".format(i), file.read())

zipped_file.seek(0)

Если вы хотите защитить свой код в будущем (подсказка подсказки Python 3), вы можете переключиться на использование io.BytesIO вместо StringIO, поскольку Python 3 полностью посвящен байтам. Другим преимуществом является то, что явные поиски не нужны с io.BytesIO перед чтением (я не проверял это поведение с HttpResponse Джанго, поэтому я оставил этот последний поиск там на всякий случай).

import io
import zipfile

zipped_file = io.BytesIO()
with zipfile.ZipFile(zipped_file, 'w') as f:
    for i, file in enumerate(files):
        f.writestr("{}.csv".format(i), file.getvalue())

zipped_file.seek(0)

stdlib поставляется с модулем zipfileи основной класс, ZipFile, принимает файл или подобный файлу объект:

from zipfile import ZipFile
temp_file = StringIO.StringIO()
zipped = ZipFile(temp_file, 'w')

# create temp csv_files = [(name1, data1), (name2, data2), ... ]

for name, data in csv_files:
    data.seek(0)
    zipped.writestr(name, data.read())

zipped.close()

temp_file.seek(0)

# etc. etc.

Я не пользователь StringIO так что я могу иметь seek а также read не к месту, но, надеюсь, вы поняли идею.

def zipFiles(files):
    outfile = StringIO() # io.BytesIO() for python 3
    with zipfile.ZipFile(outfile, 'w') as zf:
        for n, f in enumarate(files):
            zf.writestr("{}.csv".format(n), f.getvalue())
    return outfile.getvalue()

zipped_file = zip_files(myfiles)
response = HttpResponse(zipped_file, content_type='application/octet-stream')
response['Content-Disposition'] = 'attachment; filename=my_file.zip'

StringIO имеет getvalue метод, который возвращает все содержимое. Вы можете сжать ZIP-файл zipfile.ZipFile(outfile, 'w', zipfile.ZIP_DEFLATED), Значение сжатия по умолчанию: ZIP_STORED который создаст zip-файл без сжатия.

Другие вопросы по тегам