Pergunta

Em uma aplicação web que eu estou trabalhando, o usuário pode criar um arquivo zip de uma pasta cheia de arquivos. Aqui aqui está o código:

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)

Mas isso tem o efeito colateral desagradável de uma longa espera (10 + segundos) enquanto o arquivo zip está sendo baixado. É possível ignorar isso? Em vez de salvar o arquivo em um arquivo, é possível enviá-lo diretamente para o usuário?

Eu faço beleive que torrentflux fornece esse recurso excat que estou falando. Ser capaz de zip GBs de dados e baixá-lo dentro de um segundo.

Foi útil?

Outras dicas

Como mandrake diz, construtor do HttpResponse aceita objetos iteráveis.

Felizmente, ZIP formato é tal que arquivo pode ser criado em única passagem, ficha diretório central está localizado no final do arquivo:

enter descrição da imagem aqui

(Imagem de Wikipedia )

E, felizmente, zipfile na verdade não faz qualquer procura, desde que você só adicionar arquivos.

Aqui está o código que eu vim acima com. Algumas notas:

  • Eu estou usando este código para fechar um monte de imagens JPEG. Não há nenhum ponto compressão -los, estou usando ZIP apenas como recipiente.
  • O uso de memória é O (size_of_largest_file) não O (size_of_archive). E isso é bom o suficiente para mim: muitos arquivos relativamente pequenos que somam potencialmente enorme arquivo
  • Este código não define cabeçalho Content-Length, assim o usuário não recebe indicação de progresso agradável. É deve ser possível para calcular isso com antecedência, se são conhecidos tamanhos de todos os arquivos.
  • Servindo a ZIP direto para o usuário, como isso significa que currículo em downloads não vai funcionar.

Então, aqui vai:

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

Aqui está uma simples função de visualização Django que fecha-se (como um exemplo) quaisquer arquivos legíveis em /tmp e retorna o arquivo 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

É claro que esta abordagem só vai funcionar se os arquivos zip irá convenientemente caber na memória - se não, você terá que usar um arquivo de disco (que você está tentando evitar). Nesse caso, basta substituir o file = StringIO() com file = open('/path/to/yourfiles.zip', 'wb') e substituir o file.getvalue() com código para ler o conteúdo do arquivo em disco.

A biblioteca zip você estiver usando permitir a saída para um fluxo. Você poderia transmitir diretamente para o usuário em vez de escrever temporariamente em um arquivo zip ENTÃO streaming para o usuário.

É possível passar um iterador para o construtor de um HttpResponse (ver docs) . Isso permitiria que você criar um iterador personalizado que gera dados como está sendo solicitado. No entanto, eu não acho que isso vai funcionar com um zip (você teria que enviar zip parcial como está sendo criado).

A maneira correta, eu acho, seria criar os arquivos off-line, em um processo separado. O usuário pode, em seguida, monitorar o progresso e, em seguida, baixar o arquivo quando o pronto (possivelmente usando o método iterator descrito acima). Isto seria semelhante que sites como o youtube uso quando você carregar um arquivo e esperar por ele para ser processado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top