Im going over the django documentation and I found this piece of code that allows you to render a file as attachment
dl = loader.get_template('files/foo.zip')
context = RequestContext(request)
response = HttpResponse(dl.render(context), content_type = 'application/force-download')
response['Content-Disposition'] = 'attachment; filename="%s"' % 'foo.zip'
return response
The foo.zip file was created using pythons zipfile.ZipFile().writestr method
zip = zipfile.ZipFile('foo.zip', 'a', zipfile.ZIP_DEFLATED)
zipinfo = zipfile.ZipInfo('helloworld.txt', date_time=time.localtime(time.time()))
zipinfo.create_system = 1
zip.writestr(zipinfo, StringIO.StringIO('helloworld').getvalue())
zip.close()
But when I tried the code above to render the file, Im getting this error
'utf8' codec can't decode byte 0x89 in position 10: invalid start byte
Any suggestions on how to do this right?
I think what you want is to serve a file for people to download it. If that's so, you don't need to render the file, it's not a template, you just need to serve it as attachment using Django's HttpResponse:
zip_file = open(path_to_file, 'r')
response = HttpResponse(zip_file, content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename="%s"' % 'foo.zip'
return response
FileResponse is preferred over HttpResponse for binary files. Also, opening the file in 'rb' mode prevents UnicodeDecodeError.
zip_file = open(path_to_file, 'rb')
return FileResponse(zip_file)
Related
I want to download one directory from my server on button click. The directory should be downloaded in zip format. I am using Django and Python. I tried this earlier with the same code but it was on Python2 venv. The same code on Python3 venv gives utf-8 codec can't decode byte error. The zip of the directory is created successfully but when i press the download button on my website it throws me above error.
#login_required
def logs_folder_index(request):
user = request.user
if not is_moderator(user):
raise Http404("You are not allowed to see this page.")
else:
if os.path.exists('Experiments.zip'):
os.remove('Experiments.zip')
zipf = zipfile.ZipFile('Experiments.zip','w',zipfile.ZIP_DEFLATED)
path = settings.BASE_DIR + '/experiments/'
zipdir(path,zipf)
zipf.close()
zip_file = open('Experiments.zip','r')
response = HttpResponse(zip_file,
content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename="{0}"'\
.format(Experiments.zip)
return response
Can someone please help me with this problem.
Your read the file as a text stream (since the mode is 'r', and not 'rb'). Since zips are typically not encoded in UTF-8 (or any text codec in general), it is likely to eventually reach a byte sequence that can not be decoded (or will be decoded non-sensical), you thus should read it as a binary file:
#login_required
def logs_folder_index(request):
user = request.user
if not is_moderator(user):
raise Http404("You are not allowed to see this page.")
elif os.path.exists('Experiments.zip'):
os.remove('Experiments.zip')
with zipfile.ZipFile('Experiments.zip','w',zipfile.ZIP_DEFLATED) as zipf:
path = settings.BASE_DIR + '/experiments/'
zipdir(path,zipf)
with open('Experiments.zip','rb') as stream:
response = HttpResponse(stream, content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename="Experiments.zip"'
return response
I am trying to serve a .json file through this function. The problem is that every time I make the request the browser displays the content instead of downloading the file.
I think it could be due to the fact that I am using .read() as a parameter for the HttpResponse object constructor. However, if I use only the file object, I get the following exception:
TypeError: cannot serialize '_io.BufferedRandom' object
Code
try:
invoices = models.Invoice.objects.filter(pk__in=document_ids).order_by(*ordering)
pcustomers = models.CustomerProxy.objects.all()
mixed_query = list(invoices) + list(pcustomers)
file = tempfile.NamedTemporaryFile(suffix='.json')
file.write(serializers.serialize('json', mixed_query).encode())
file.seek(0)
response = HttpResponse(file.read(), content_type='application/json')
response['Content-Disposition'] = 'attachment; filename=%s' % file.name
response['Content-Length'] = os.path.getsize(file.name)
except Exception:
raise
return response
You don't need to go through the whole file generation process to create a downloadable file, you just need to add the Content-Disposition header normally. Does the code below work?
...
mixed_query = list(invoices) + list(pcustomers)
json_str = serializers.serialize('json', mixed_query))
response = HttpResponse(json_str, content_type='application/json')
response['Content-Disposition'] = 'attachment; filename=export.json'
Based on the code that you show, you do not need to write to a temporary file. Why don't you just pass the result of serialize() into HttpResponse()?
response = HttpResponse(serializers.serialize('json', mixed_query), content_type='application/json')
You can set the attachment name to whatever you like, something descriptive would seem to be better than the random alphanumeric string generated by tempfile.NamedTemporaryFile().
response['Content-Disposition'] = 'attachment; filename="invoices_and_customers.json"'
If you really want to specify the length:
response['Content-Length'] = len(response.content)
or you could add the ConditionalGetMiddleware middleware to your settings and have Django add the Content-Length for you.
add this to your Http response
HttpResponse(mimetype='application/force-download')
I'm trying to serve a txt file generated with some content and i am having some issues. I'vecreated the temp files and written the content using NamedTemporaryFile and just set delete to false to debug however the downloaded file does not contain anything.
My guess is the response values are not pointed to the correct file, hense nothing is being downloaded, heres my code:
f = NamedTemporaryFile()
f.write(p.body)
response = HttpResponse(FileWrapper(f), mimetype='application/force-download')
response['Content-Disposition'] = 'attachment; filename=test-%s.txt' % p.uuid
response['X-Sendfile'] = f.name
Have you considered just sending p.body through the response like this:
response = HttpResponse(mimetype='text/plain')
response['Content-Disposition'] = 'attachment; filename="%s.txt"' % p.uuid
response.write(p.body)
XSend requires the path to the file in
response['X-Sendfile']
So, you can do
response['X-Sendfile'] = smart_str(path_to_file)
Here, path_to_file is the full path to the file (not just the name of the file)
Checkout this django-snippet
There can be several problems with your approach:
file content does not have to be flushed, add f.flush() as mentioned in comment above
NamedTemporaryFile is deleted on closing, what might happen just as you exit your function, so the webserver has no chance to pick it up
temporary file name might be out of paths which web server is configured to send using X-Sendfile
Maybe it would be better to use StreamingHttpResponse instead of creating temporary files and X-Sendfile...
import urllib2;
url ="http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=s&chld=H|0";
opener = urllib2.urlopen(url);
mimetype = "application/octet-stream"
response = HttpResponse(opener.read(), mimetype=mimetype)
response["Content-Disposition"]= "attachment; filename=aktel.png"
return response
I am using PYTHON+DJANGO to implement a file sharing system. When a user attempts to download a file it works well in Chrome and IE but not Firefox, Firefox returns part of the file name and no extension if it does not recognize the extension (e.g. .pl and .csv)
View
filename = os.path.join(MEDIA_ROOT, entry.myfile.url)
wrapper = FileWrapper(file(filename,'rb'))
response = HttpResponse(wrapper, content_type='application/octet-stream')
response['Content-Length'] = os.path.getsize(filename)
response['Content-Disposition'] = "attachment; filename=" + entry.name
I tried content_type=mimetypes.guess_type(filename) but this didn't solve the problem
I also tried replacing any spaces within the filename with periods, that did work! But I'm a sure there is a clean solution!!
Answer to an old question, I know, but the actual issue is that you didn't enclose the filename with double quotes (and it has to be double, not single). IE and Chrome will read until the end of the line, but Firefox will read until the first space and stop.
So just change response['Content-Disposition'] = "attachment; filename=" + entry.name to response['Content-Disposition'] = 'attachment; filename="%s"'%(entry.name) and you're set.
Based on django.views.static:
import mimetypes
import os
import stat
from django.http import HttpResponse
statobj = os.stat(fullpath)
mimetype, encoding = mimetypes.guess_type(fullpath)
mimetype = mimetype or 'application/octet-stream'
with open(fullpath, 'rb') as f:
response = HttpResponse(f.read(), mimetype=mimetype)
if stat.S_ISREG(statobj.st_mode):
response["Content-Length"] = statobj.st_size
if encoding:
response["Content-Encoding"] = encoding
response['Content-Disposition'] = 'inline; filename=%s'%os.path.basename(fullpath)
return response
I am trying to create a download of a file object. the file was added using django-filebrowser which means it is turn in to a string path to the the file. I have tried the following:
f = Obj.objects.get(id=obj_id)
myfile = FileObject(os.path.join(MEDIA_ROOT, f.Audio.path))
...
response = HttpResponse(myfile, content_type="audio/mpeg")
response['Content-Disposition'] = 'attachment; filename=myfile.mp3'
return response
The file that is downloaded contains the string of the path to the file location and not the file. Could anyone be of assistance on how to access the file object?
f = Obj.objects.get(id=obj_id)
myfile = open(os.path.join(MEDIA_ROOT, f.Audio.path)).read()
...
response = HttpResponse(myfile, content_type="audio/mpeg")
response['Content-Disposition'] = 'attachment; filename=myfile.mp3'
return response
NOTE! This is not memory friendly! Since the whole file is put into memory. You're better of using a webserver for file serving or if you want to use Django for file serving you could use xsendfile or have a look at this thread
You need to open the file and send it's binary contents back in the response. So something like:
fileObject = FileObject(os.path.join(MEDIA_ROOT, f.Audio.path))
myfile = open(fileObject.path)
response = HttpResponse(myfile.read(), mimetype="audio/mpeg")
response['Content-Disposition'] = 'attachment; filename=myfile.mp3'
return response
Hope that gets what you're looking for.