Отправка нескольких файлов.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-файл без сжатия.