Servindo arquivos ZIP geradas dinamicamente em Django

votos
47

Como servir aos usuários um arquivo ZIP gerado dinamicamente em Django?

Eu estou fazendo um site, onde os usuários podem escolher qualquer combinação de livros disponíveis e baixá-los como arquivo ZIP. Estou preocupado que gerar tais arquivos para cada pedido iria atrasar o meu servidor para baixo para um rastreamento. Eu também ouvi que o Django não tem atualmente uma boa solução para servir arquivos gerados dinamicamente.

Publicado 15/09/2008 em 23:00
fonte usuário
Em outras línguas...                            


10 respostas

votos
39

A solução é como se segue.

Use Python módulo zipfile para criar arquivo zip, mas como o arquivo especificar StringIO objeto (ZipFile construtor requer objeto de arquivo-like). Adicionar arquivos que você deseja compactar. Então, em sua aplicação Django retornar o conteúdo de StringIO objeto em HttpResponsecom mimetype definido para application/x-zip-compressed(ou, pelo menos application/octet-stream). Se você quiser, você pode definir content-dispositioncabeçalho, mas isso não deve ser realmente necessário.

Mas cuidado, a criação de arquivos zip em cada pedido é má idéia e isso pode matar seus (timeouts sem contar se os arquivos são grandes) do servidor. abordagem em termos de performance é armazenar em cache de saída gerado em algum lugar no sistema de arquivos e regenerá-lo somente se arquivos de origem mudaram. Ainda melhor ideia é preparar arquivos de antecedência (por exemplo. De trabalho cron) e ter o seu servidor web servi-los como estática habituais.

Respondeu 16/09/2008 em 14:30
fonte usuário

votos
38

Aqui está uma visão Django para fazer isso:

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
Respondeu 18/10/2012 em 10:32
fonte usuário

votos
6

O Django não lidar diretamente a geração de conteúdo dinâmico (especificamente arquivos zip). Esse trabalho seria feito pela biblioteca padrão do Python. Você pode dar uma olhada em como criar dinamicamente um arquivo Zip em Python aqui .

Se você está preocupado com isso abrandar o seu servidor, você pode armazenar em cache os pedidos, se você espera ter muitos dos mesmos pedidos. Você pode usar do Django framework de cache para ajudá-lo com isso.

No geral, arquivos fechando pode ser intensivo da CPU, mas Django não deve ser mais lento do que o outro framework web Python.

Respondeu 15/09/2008 em 23:09
fonte usuário

votos
5

Para python3 eu uso o io.ByteIO desde StringIO é obsoleto para conseguir isso. Espero que ajude.

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
Respondeu 17/10/2016 em 00:10
fonte usuário

votos
5

Shameless plug: você pode usar django-zipview para o mesmo fim.

Depois de um 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]
Respondeu 30/10/2014 em 16:24
fonte usuário

votos
2

Muitas respostas aqui sugerem a utilização de um StringIOou BytesIOtampão. Contudo, isto não é necessário, pois HttpResponsejá é um objeto de arquivo-like:

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
Respondeu 11/03/2018 em 12:32
fonte usuário

votos
2

Este módulo gera e transmite um arquivo: https://github.com/allanlei/python-zipstream

(Eu não estou ligado ao desenvolvimento. Só de pensar em usá-lo.)

Respondeu 12/06/2014 em 08:39
fonte usuário

votos
1

I utilizado Django 2.0 e 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
Respondeu 02/08/2018 em 13:56
fonte usuário

votos
1

Eu sugiro usar o modelo separado para armazenar esses arquivos zip temporários. Você pode criar zip no-fly, salvar para modelar com FileField e, finalmente, enviar url para o usuário.

vantagens:

  • Servindo arquivos zip estáticas com mecanismo media Django (como uploads habituais).
  • Capacidade de limpeza de arquivos zip obsoletos pela execução do script cron regulares (que pode usar campo de data a partir do modelo de arquivo zip).
Respondeu 16/09/2008 em 16:31
fonte usuário

votos
0

Você não pode simplesmente escrever um link para um "servidor zip" ou outros enfeites? Por que arquivar o zip em si precisa ser servido a partir Django? roteiro era CGI A 90 de gerar um zip e cuspi-lo para stdout é realmente tudo o que é necessário aqui, pelo menos tanto quanto eu posso ver.

Respondeu 15/09/2008 em 23:03
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more