8

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!

asked Mar 17, 2010 at 18:33

6 Answers 6

11

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:

  1. Create it in memory and serve it from django.
  2. Create it on the disk, and return a HttpResponseRedirect to it, so that your webserver deals with the download itself (if the file is very large, you should use this option).

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

answered Mar 17, 2010 at 21:36
Sign up to request clarification or add additional context in comments.

1 Comment

It is generated on the fly for "this" request and stored temporarily under /static/.
6

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
answered Mar 17, 2010 at 18:47

3 Comments

tried it, still does not work. I can see the response and headers in FireBug but I do not get a dialog box.
I tried it but didn't work. Please see my reply, I found out the issue has nothing to do with django. Thanks
If you use my method with a normal link, it'll have the same effect as ajax in that it won't move away from the current page.
3

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!

answered Mar 18, 2010 at 17:42

Comments

1

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.

answered Jul 2, 2010 at 16:40

Comments

0

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"'
answered Mar 17, 2010 at 22:14

1 Comment

I had to enclose the filename with quotes to make it work.
0

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
answered Oct 19, 2022 at 11:01

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.