문제

내가 작업중인 웹 앱에서 사용자는 파일로 가득 찬 폴더의 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 초 안에 다운로드 할 수 있습니다.

도움이 되었습니까?

다른 팁

Mandrake가 말했듯이, Httpresponse의 생성자는 반복 가능한 객체를 수용합니다.

운 좋게도 Zip 형식은 아카이브가 단일 패스로 생성 될 수 있도록, 중앙 디렉토리 레코드는 파일 끝에 있습니다.

enter image description here

(사진에서 위키 백과)

그리고 운 좋게도, 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와 같은 사이트가 사용하는 사이트와 비슷합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top