Há pouco tempo atrás eu postei aqui no blog como forçar o download de arquivos com Django.
Na época eu havia testado o sistema em todos os navegadores que estavam ao meu alcance (Firefox, Iceweasel, Opera, Epiphany, IE6 e IE7) porém mesmo assim não precisou de muito tempo em ambiente de produção para me contactarem dizendo que o sistema não estava funcionando no IE7 (ué, como assim!? mas eu tinha testado isso!).
Testei novamente e então descobri que na verdade ele funcionava e não funcionava. Hm, ok… mas como assim!? Na verdade ele funcionava perfeitamente no IE7 do Windows 2003 Server, mas não funcionava em nenhum outro e eu dei o azar (ou sorte) de testar justamente nele… no Windows 2003 Server. Nos Windows com problemas (pleonasmo?) o erro que ocorria era o seguinte:
Internet Explorer was not able to open this Internet site. The requested site is either unavailable or cannot be found. Please try again later.
Ok, um erro muito explicativo… Então vamos ao Google! Depois de muito procurar no Google sem encontrar nenhuma solução, um colega de empresa decidiu comparar os headers de uma aplicação escrita em .NET e que funcionava em qualquer IE, com os headers gerados pela minha aplicação. Chegamos a conclusão de que o problema estava no campo Vary, que aparecia nos headers do Django mas não existia no header da aplicação .NET.
A primeira idéia foi remover o campo manualmente, antes de retornar o response da página:
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-Disposition'] = 'attachment; filename=%s' % file
response['Content-Type'] = mimetype
response['Content-Length'] = str(path.getsize(abspath))
del response['Vary']
return response
* As linhas em negritos são as linhas que foram alteradas na solução inicial.
Mas não funcionou… O campo Vary continuava aparecendo e com o mesmo valor de antes. Lendo um pouco de documentação, descobrimos que essa diretiva não pode ser alterada diretamente no header da página e também qual era a forma correta de remover esse campo:
from django.views.decorators.cache import cache_control
from django.http import HttpResponse, Http404
from os import path
import mimetypes
@cache_control(private=True)
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-Disposition'] = 'attachment; filename=%s' % file
response['Content-Type'] = mimetype
response['Content-Length'] = str(path.getsize(abspath))
return response
* As linhas em negritos são as linhas que foram alteradas na solução inicial.
Ok, agora sim! Tudo funcionando direitinho e em todos os (pseudo-)navegadores!
Como eu não achei nada na internet que ligasse esse problema aos campos Vary do header, acredito que esse post possa ajudar alguém mais que esteja passando pelo menos problema! :)
UPDATE: Em alguns casos a conexão terminava antes de ter baixado todo o arquivo. Esse é um problema do servidor, e para corrigi-lo basta definir a diretiva EnableSendfile do Apache como On. Com essa configuração, também houve uma melhora significativa na taxa de download do servidor (pelo menos no meu caso).
