Reputation: 235
I am trying to download a CSV file using HttpResponse to make sure that the browser treats it as an attachment. I follow the instructions provided here but my browser does not prompt a "Save As" dialog. I cannot figure out what is wrong with my function. All help is appreciated.
dev savefile(request): try: myfile = request.GET['filename'] filepath = settings.MEDIA_ROOT + 'results/' destpath = os.path.join(filepath, myfile) response = HttpResponse(FileWrapper(file(destpath)), mimetype='text/csv' ) response['Content-Disposition'] = 'attachment; filename="%s"' %(myfile) return response except Exception, err: errmsg = "%s"%(err) return HttpResponse(errmsg)
Happy Pat's day!
Upvotes: 8
Views: 23044
Reputation: 101
Try this
def serve_tsv(request):
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(
content_type='text/csv',
headers={'Content-Disposition': 'attachment; filename="somefilename.csv"'},
)
writer = csv.writer(response)
writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])
writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"])
return response
Upvotes: 0
Reputation: 24823
If the file is static (i.e not generated specifically for this request) you shouldn't be serving it through django anyway. You should configure some path (like /static/) to be served by your webserver, and save all the django overhead.
If the file is dynamic, there are 2 options:
As for serving it dynamically, I've been using the following code (which is a simplified version of ExcelResponse)
import StringIO
from django.db.models.query import ValuesQuerySet, QuerySet
class CSVResponse(HttpResponse):
def __init__(self, data, output_name='data', headers=None, encoding='utf8'):
# Make sure we've got the right type of data to work with
valid_data = False
if isinstance(data, ValuesQuerySet):
data = list(data)
elif isinstance(data, QuerySet):
data = list(data.values())
if hasattr(data, '__getitem__'):
if isinstance(data[0], dict):
if headers is None:
headers = data[0].keys()
data = [[row[col] for col in headers] for row in data]
data.insert(0, headers)
if hasattr(data[0], '__getitem__'):
valid_data = True
assert valid_data is True, "CSVResponse requires a sequence of sequences"
output = StringIO.StringIO()
for row in data:
out_row = []
for value in row:
if not isinstance(value, basestring):
value = unicode(value)
value = value.encode(encoding)
out_row.append(value.replace('"', '""'))
output.write('"%s"\n' %
'","'.join(out_row))
mimetype = 'text/csv'
file_ext = 'csv'
output.seek(0)
super(CSVResponse, self).__init__(content=output.getvalue(),
mimetype=mimetype)
self['Content-Disposition'] = 'attachment;filename="%s.%s"' % \
(output_name.replace('"', '\"'), file_ext)
To use it, just use return CSVResponse(...) passing in a list of lists, a list of dicts (with same keys), a QuerySet, a ValuesQuerySet
Upvotes: 11
Reputation: 235
Thomas, I had used an Ajax function to save and download this file. It appears that in such a case the "Save As " box will not appear regardless of the headers. I simply used javascript to download that file. window.open("path/to/file"); and it does the trick. I tested on IE6 and Firefox and the dialog box appears.
Upvotes: 1
Reputation: 235
Thanks everyone for your suggestions. I picked a few new tricks :) However I think I have found the answer to my problem here: Downloading CSV via AJAX My "savefile" function is called via an Ajax request and it appears that ajax has a limitation where"save as dialog box" does not appear no matter what the HTTP headers are.
I should have mentioned that I use Ajax to call this function but it never occurred to me that this could be an issue.:) Thankyou StackOverflow!
Upvotes: 3
Reputation: 64739
Did you try specifying the content-type? e.g.
response['Content-Type'] = 'application/x-download';
Edit:
Note, this code successfully triggers a "Save As" dialog for me. Note I specify "application/x-download" directly in the mimetype argument. You also might want to recheck your code, and ensure your file path is correct, and that FileWrapper() isn't doing something weird.
def save_file(request):
data = open(os.path.join(settings.PROJECT_PATH,'data/table.csv'),'r').read()
resp = django.http.HttpResponse(data, mimetype='application/x-download')
resp['Content-Disposition'] = 'attachment;filename=table.csv'
return resp
Upvotes: 6
Reputation: 1173
Does it make any difference if you don't enclose the filename in double quotes? The sample code does not quote the filename:
response['Content-Disposition'] = 'attachment; filename=foo.xls'
but your code does:
response['Content-Disposition'] = 'attachment; filename="foo.xls"'
Upvotes: 0