즉각적인 다운로드를위한 Zip Archive를 만듭니다
문제
내가 작업중인 웹 앱에서 사용자는 파일로 가득 찬 폴더의 zip 아카이브를 만들 수 있습니다. 여기에 코드가 있습니다.
files = torrent[0].files
zipfile = z.ZipFile(zipname, 'w')
output = ""
for f in files:
zipfile.write(settings.PYRAT_TRANSMISSION_DOWNLOAD_DIR + "/" + f.name, f.name)
downloadurl = settings.PYRAT_DOWNLOAD_BASE_URL + "/" + settings.PYRAT_ARCHIVE_DIR + "/" + filename
output = "Download <a href=\"" + downloadurl + "\">" + torrent_name + "</a>"
return HttpResponse(output)
그러나 이것은 Zip 아카이브가 다운로드되는 동안 긴 대기 (10 초)의 불쾌한 부작용이 있습니다. 이것을 건너 뛸 수 있습니까? 아카이브를 파일에 저장하는 대신 사용자에게 바로 보낼 수 있습니까?
Torrentflux가 내가 말하는이 Excat 기능을 제공한다는 것을 믿습니다. GBS의 데이터를 지퍼하고 1 초 안에 다운로드 할 수 있습니다.
해결책
이것을 확인하십시오 Django에서 동적으로 생성 된 지퍼 보관소를 제공합니다
다른 팁
Mandrake가 말했듯이, Httpresponse의 생성자는 반복 가능한 객체를 수용합니다.
운 좋게도 Zip 형식은 아카이브가 단일 패스로 생성 될 수 있도록, 중앙 디렉토리 레코드는 파일 끝에 있습니다.
(사진에서 위키 백과)
그리고 운 좋게도, zipfile
실제로 파일 만 추가하는 한 아무것도 찾지 못합니다.
여기 내가 생각해 낸 코드가 있습니다. 몇 가지 메모 :
- 이 코드를 사용하여 많은 JPEG 사진을 ZIPPING합니다. 요점이 없습니다 압축 그것들은 Zip을 컨테이너로만 사용하고 있습니다.
- 메모리 사용량은 O (size_of_largest_file)가 아닙니다. 그리고 이것은 나에게 충분합니다 : 잠재적으로 거대한 아카이브에 추가되는 많은 비교적 작은 파일
- 이 코드는 컨텐츠 길이 헤더를 설정하지 않으므로 사용자는 진행 상황 표시를 얻지 못합니다. 그것 가능해야합니다 모든 파일의 크기가 알려진 경우이를 미리 계산합니다.
- 이와 같이 사용자에게 직접 지퍼를 제공한다는 것은 다운로드의 이력서가 작동하지 않는다는 것을 의미합니다.
그래서 여기에 간다 :
import zipfile
class ZipBuffer(object):
""" A file-like object for zipfile.ZipFile to write into. """
def __init__(self):
self.data = []
self.pos = 0
def write(self, data):
self.data.append(data)
self.pos += len(data)
def tell(self):
# zipfile calls this so we need it
return self.pos
def flush(self):
# zipfile calls this so we need it
pass
def get_and_clear(self):
result = self.data
self.data = []
return result
def generate_zipped_stream():
sink = ZipBuffer()
archive = zipfile.ZipFile(sink, "w")
for filename in ["file1.txt", "file2.txt"]:
archive.writestr(filename, "contents of file here")
for chunk in sink.get_and_clear():
yield chunk
archive.close()
# close() generates some more data, so we yield that too
for chunk in sink.get_and_clear():
yield chunk
def my_django_view(request):
response = HttpResponse(generate_zipped_stream(), mimetype="application/zip")
response['Content-Disposition'] = 'attachment; filename=archive.zip'
return response
다음은 읽을 수있는 파일을 (예를 들어)로 올리는 간단한 django보기 기능입니다. /tmp
zip 파일을 반환합니다.
from django.http import HttpResponse
import zipfile
import os
from cStringIO import StringIO # caveats for Python 3.0 apply
def somezip(request):
file = StringIO()
zf = zipfile.ZipFile(file, mode='w', compression=zipfile.ZIP_DEFLATED)
for fn in os.listdir("/tmp"):
path = os.path.join("/tmp", fn)
if os.path.isfile(path):
try:
zf.write(path)
except IOError:
pass
zf.close()
response = HttpResponse(file.getvalue(), mimetype="application/zip")
response['Content-Disposition'] = 'attachment; filename=yourfiles.zip'
return response
물론이 접근법은 Zip 파일이 메모리에 편리하게 맞는 경우에만 작동합니다. 그렇지 않은 경우 디스크 파일을 사용해야합니다 (피하려고하는 디스크 파일). 이 경우, 당신은 그냥 교체합니다 file = StringIO()
~와 함께 file = open('/path/to/yourfiles.zip', 'wb')
그리고 교체 file.getvalue()
디스크 파일의 내용을 읽는 코드가 있습니다.
사용중인 Zip 라이브러리가 스트림에 출력 할 수 있도록합니다. zip 파일에 일시적으로 작성 한 다음 사용자에게 스트리밍하는 대신 사용자에게 직접 스트리밍 할 수 있습니다.
반복자를 HTTPRPRESPonse의 생성자에게 전달할 수 있습니다. (문서 참조). 그러면 요청대로 데이터를 생성하는 사용자 정의 반복기를 만들 수 있습니다. 그러나 나는 그것이 zip과 함께 작동하지 않을 것이라고 생각합니다 (당신은 그것이 만들어 질 때 부분 지퍼를 보내야 할 것입니다).
올바른 방법은 별도의 프로세스에서 오프라인 파일을 만드는 것입니다. 그런 다음 사용자는 진행 상황을 모니터링 한 다음 준비되면 파일을 다운로드 할 수 있습니다 (위에서 설명한 반복 메소드를 사용하여). 파일을 업로드하고 처리 될 때까지 YouTube와 같은 사이트가 사용하는 사이트와 비슷합니다.