Django에서 동적으로 생성 된 ZIP 아카이브 제공
Django에서 동적으로 생성 된 ZIP 아카이브를 사용자에게 제공하는 방법은 무엇입니까?
사용자가 사용 가능한 책의 조합을 선택하고 ZIP 아카이브로 다운로드 할 수있는 사이트를 만들고 있습니다. 각 요청에 대해 이러한 아카이브를 생성하면 서버가 크롤링되는 속도가 느려 질까 걱정됩니다. 또한 Django는 현재 동적으로 생성 된 파일을 제공하는 데 좋은 솔루션이 없다고 들었습니다.
해결책은 다음과 같습니다.
Python 모듈 zipfile 을 사용하여 zip 아카이브를 생성하지만 파일로 StringIO 객체를 지정 합니다 (ZipFile 생성자에는 파일과 유사한 객체가 필요함). 압축하려는 파일을 추가하십시오. 그런 다음 Django 응용 프로그램에서 HttpResponse
mimetype이 application/x-zip-compressed
(또는 적어도 application/octet-stream
)로 설정된 StringIO 객체의 내용을 반환합니다 . 원하는 경우 content-disposition
헤더 를 설정할 수 있지만 실제로는 필요하지 않습니다.
그러나 각 요청에 대해 zip 아카이브를 만드는 것은 나쁜 생각이며 이로 인해 서버가 중단 될 수 있습니다 (아카이브가 큰 경우 시간 초과를 계산하지 않음). 성능 측면에서는 생성 된 출력을 파일 시스템의 어딘가에 캐시하고 소스 파일이 변경된 경우에만 다시 생성하는 것입니다. 더 나은 아이디어는 사전에 아카이브를 준비하고 (예 : cron 작업으로) 웹 서버가이를 일반적인 통계로 제공하도록하는 것입니다.
이를 수행하는 Django 뷰는 다음과 같습니다.
import os
import zipfile
import StringIO
from django.http import HttpResponse
def getfiles(request):
# Files (local path) to put in the .zip
# FIXME: Change this (get paths from DB etc)
filenames = ["/tmp/file1.txt", "/tmp/file2.txt"]
# Folder name in ZIP archive which contains the above files
# E.g [thearchive.zip]/somefiles/file2.txt
# FIXME: Set this to something better
zip_subdir = "somefiles"
zip_filename = "%s.zip" % zip_subdir
# Open StringIO to grab in-memory ZIP contents
s = StringIO.StringIO()
# The zip compressor
zf = zipfile.ZipFile(s, "w")
for fpath in filenames:
# Calculate path for file in zip
fdir, fname = os.path.split(fpath)
zip_path = os.path.join(zip_subdir, fname)
# Add file, at correct path
zf.write(fpath, zip_path)
# Must close zip for all contents to be written
zf.close()
# Grab ZIP file from in-memory, make response with correct MIME-type
resp = HttpResponse(s.getvalue(), mimetype = "application/x-zip-compressed")
# ..and correct content-disposition
resp['Content-Disposition'] = 'attachment; filename=%s' % zip_filename
return resp
여기에 많은 답변이 StringIO
또는 BytesIO
버퍼 사용을 제안합니다 . 그러나 이것은 HttpResponse
이미 파일과 같은 객체이므로 필요하지 않습니다 .
response = HttpResponse(content_type='application/zip')
zip_file = zipfile.ZipFile(response, 'w')
for filename in filenames:
zip_file.write(filename)
response['Content-Disposition'] = 'attachment; filename={}'.format(zipfile_name)
return response
python3의 경우 StringIO 가 사용되지 않기 때문에 io.ByteIO를 사용합니다 . 도움이 되었기를 바랍니다.
import io
def my_downloadable_zip(request):
zip_io = io.BytesIO()
with zipfile.ZipFile(zip_io, mode='w', compression=zipfile.ZIP_DEFLATED) as backup_zip:
backup_zip.write('file_name_loc_to_zip') # u can also make use of list of filename location
# and do some iteration over it
response = HttpResponse(zip_io.getvalue(), content_type='application/x-zip-compressed')
response['Content-Disposition'] = 'attachment; filename=%s' % 'your_zipfilename' + ".zip"
response['Content-Length'] = zip_io.tell()
return response
Django는 동적 콘텐츠 (특히 Zip 파일) 생성을 직접 처리하지 않습니다. 이 작업은 Python의 표준 라이브러리에서 수행됩니다. 여기 에서 Python 에서 Zip 파일을 동적으로 만드는 방법을 살펴볼 수 있습니다 .
서버 속도가 느려지는 것이 걱정된다면 동일한 요청이 많을 것으로 예상되면 요청을 캐시 할 수 있습니다. Django의 캐시 프레임 워크 를 사용 하여 도움을받을 수 있습니다.
Overall, zipping files can be CPU intensive but Django shouldn't be any slower than another Python web framework.
Shameless plug: you can use django-zipview for the same purpose.
After a pip install django-zipview
:
from zipview.views import BaseZipView
from reviews import Review
class CommentsArchiveView(BaseZipView):
"""Download at once all comments for a review."""
def get_files(self):
document_key = self.kwargs.get('document_key')
reviews = Review.objects \
.filter(document__document_key=document_key) \
.exclude(comments__isnull=True)
return [review.comments.file for review in reviews if review.comments.name]
I used Django 2.0 and Python 3.6.
import zipfile
import os
from io import BytesIO
def download_zip_file(request):
filelist = ["path/to/file-11.txt", "path/to/file-22.txt"]
byte_data = BytesIO()
zip_file = zipfile.ZipFile(byte_data, "w")
for file in filelist:
filename = os.path.basename(os.path.normpath(file))
zip_file.write(file, filename)
zip_file.close()
response = HttpResponse(byte_data.getvalue(), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=files.zip'
# Print list files in zip_file
zip_file.printdir()
return response
This module generates and streams an archive: https://github.com/allanlei/python-zipstream
(I'm not connected to the development. Just thinking about using it.)
I suggest to use separate model for storing those temp zip files. You can create zip on-fly, save to model with filefield and finally send url to user.
Advantages:
- Serving static zip files with django media mechanism (like usual uploads).
- Ability to cleanup stale zip files by regular cron script execution (which can use date field from zip file model).
Can't you just write a link to a "zip server" or whatnot? Why does the zip archive itself need to be served from Django? A 90's era CGI script to generate a zip and spit it to stdout is really all that's required here, at least as far as I can see.
ReferenceURL : https://stackoverflow.com/questions/67454/serving-dynamically-generated-zip-archives-in-django
'IT Share you' 카테고리의 다른 글
Visual Studio 2017 및 2019는 스크롤 막대에서 선택한 단어를 강조 표시합니다. (0) | 2021.01.06 |
---|---|
vuejs 라우터의 선택적 매개 변수 (0) | 2021.01.06 |
파이썬에서 숫자의 비트 표현 인쇄 (0) | 2021.01.06 |
jConsole을 사용하여 원격 Tomcat JMX 인스턴스 연결 (0) | 2021.01.06 |
CSS를 사용하여 HTML 텍스트 영역에서 고정 폭 글꼴을 사용하도록 강제 (0) | 2021.01.06 |