Primeiro post pelo Planeta GNU/Linux Brasil. :)
Essa semana precisei criar uma aplicação web que disponibilizasse alguns arquivos para download e então decidi fazer isso utilizando Python e Django.
Uma parte da minha aplicação consistia em forçar o browser a abrir uma janela de download ao invés de exibir o arquivo (como acontece no caso de imagens ou arquivos texto). Eu já havia feito isso algumas vezes em PHP, mas fui pesquisar para ver se o Django não possuia alguma função pronta que me facilitasse essa tarefa, algo como:
return HttpResponseFile(file)
Passei algum tempo procurando por alguma solução mais inteligente do que alterar o header na mão, mas não consegui encontrar nada. Fui então atrás da documentação oficial, para descobrir como eu podia aplicar a solução que eu já utilizava em PHP para a realidade do Django. Encontrei aqui a “receita de bolo” de como alterar o header da requisição e fazer com que o browser agisse da forma como eu queria.
Portanto segue abaixo, a parte principal da minha implementação:
urls.py
Esse arquivo tem uma única função: extrair da url o nome do arquivo a ser baixado e passa-lo como parametro para a view.
urlpatterns = patterns('',
(r'(?P<file>(^.*$)', 'myproject.apps.download.views.index'),
)
views.py
Aqui é onde a mágica ocorre… na verdade não é nada muito mágico, mas enfim. :)
Essa view apenas verifica se o arquivo existe e o envia para download, caso o arquivo não exista, retorna a página 404. Nessa função você pode aplicar várias outras validações, como verificar se o usuário está logado ou se ele relamente tem acesso a esse arquivo, etc. porém nada disso era necessário no meu caso.
from django.http import HttpResponse, Http404
from os import path
import mimetypes
def index(request, file=''):
abspath = path.join('/your/absolute/path', file)
if not (file and path.exists(abspath)):
raise Http404()
mimetype, encoding = mimetypes.guess_type(abspath)
if mimetype is None:
mimetype = 'application/force-download'
response = HttpResponse(open(abspath, 'r').read())
response['Content-Type'] = mimetype
response['Pragma'] = 'public'
response['Expires'] = '0'
response['Cache-Control'] = 'must-revalidate, post-check=0, pre-check=0'
response['Content-Disposition'] = 'attachment; filename=%s' % file
response['Content-Transfer-Encoding'] = 'binary'
response['Content-Length'] = str(path.getsize(abspath))
return response
Espero que seja de alguma utilidade… e se alguém tiver uma solução melhor! Por favor, poste nos comentários.
Não faz muito tempo que eu mexo com Django, mas estou gostando da brincadeira!
Acredito que o número de posts sobre Python desse blog tende a crescer consideravelmente… e isso é bom!
UPDATE: Depois de algum tempo com o sistema em produção, foi constatado que a solução acima não funciona em algumas versão do Internet Explorer 7. A solução do problema também foi documentada e está disponível aqui.

3 Comments
Bacana, mas acabou tendo que alterar os headers do response de qualquer forma :D
Eu gosto de criar um response:
http://dpaste.com/hold/44151/
Mas o django tem uma solução bem mais limpa pro que você quer:
http://www.djangoproject.com/documentation/static_files/
Abração!
Gabriel Falcão:
Legal a sua solução… A idéia de criar uma classe HttpResponse específica para tratar esses casos é realmente muito boa! :)
Com relação ao link do Django que você passou, eu acredito que ele não atenderia as minhas necessidades, pois eu precisava que qualquer arquivo (incluindo imagens) fossem enviados como download… Pelo que eu vi, aquilo é uma solução para evitar seja necessário um subdomain específico para arquivos estáticos.
Arthur, eu vi um ponto de melhoria para teu codigo.
…
from fratelli import settings #importa arquivo settings.py
…
abspath = path.join(settings.BASEDIR + /your/absolute/path’, file)
…
2 Trackbacks/Pingbacks
Forçando o download de arquivos com Django…
Exemplo de código de uma aplicação Django que consiste em forçar o browser a abrir uma janela de download ao invés de exibir o arquivo….
[...] Há pouco tempo atrás eu postei aqui no blog como forçar o download de arquivos com Django. [...]
Post a Comment