I am developing a function to download an image.
but I am getiing this error
coercing to Unicode: need string or buffer, ImageFieldFile found
It's the code:
def download_image(request, image_id):
img = Base.objects.get(base_id=image_id)
wrapper = FileWrapper(open(img.file)) # img.file returns full path to the image
content_type = mimetypes.guess_type(filename)[0] # Use mimetypes to get file type
response = HttpResponse(wrapper,content_type=content_type)
response['Content-Length'] = os.path.getsize(img.file)
response['Content-Disposition'] = "attachment; filename=%s" % img.name
return response
img.file is a ImageFieldFile object, open() requires a string. You need to do open(img.file.name) to get the actual file path.
Related
I am using django 1.11.2 and I want to have view with downloadable file. After click on link the file is downloading but file is empty and instead of png, I recieved empty txt file.
def download_file(request, uuid):
response = HttpResponse(content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(models.DownloadLink.objects.get(uuid=uuid).download_file.file)
response['Content-Length'] = 'http://{}{}'.format(Site.objects.get_current(), models.DownloadLink.objects.get(uuid=uuid).download_file.file.url)
return response
EDIT:
value here response['Content-Length'] is http://127.0.0.1:8000/media/filer_public/49/54/4954a7bb-8ad3-4679-9248-bffc7d186ca8/photo-105221.jpeg
Just pass the file in HttpResponse constructor like this:
def download_file(request, uuid):
file = models.DownloadLink.objects.get(uuid=uuid).download_file
response = HttpResponse(file.file, content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file.file)
response['Content-Length'] = 'http://{}{}'.format(Site.objects.get_current(), file.file.url)
return response
From HttpResponse docs
content should be an iterator or a string. If it’s an iterator, it
should return strings, and those strings will be joined together to
form the content of the response. If it is not an iterator or a
string, it will be converted to a string when accessed.
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 working with Django 1.7 and Python 3.4.
I have a model like this one:
class ImageModel(models.Model):
image = models.ImageField(verbose_name='image', upload_to='uploaded_images/')
Now I want to download image which is saved in /static/uploaded_images/.
For example I have a link like this: www.example.com/image/download/1, where 1 is id of ImageModel object.
Now i have a view:
def download_image(request, image_id):
img = ImageModel.objects.get(id=image_id)
( what I need to do? )
What next? How to create a view that will force download of that image?
You can try this code, maybe need some caveats:
from django.core.servers.basehttp import FileWrapper
import mimetypes
def download_image(request, image_id):
img = ImageModel.objects.get(id=image_id)
wrapper = FileWrapper(open(img.file)) # img.file returns full path to the image
content_type = mimetypes.guess_type(filename)[0] # Use mimetypes to get file type
response = HttpResponse(wrapper,content_type=content_type)
response['Content-Length'] = os.path.getsize(img.file)
response['Content-Disposition'] = "attachment; filename=%s" % img.name
return response
I'm assuming there is a field .name in your ImageModel to get the name of the file in the second-to-last line ...filename=%s" % img.name You should edit the code to fit your project.
There is a field in an ImageField that is file, in the code here I use img.file to get the path to the file, you should change that for img.YOUR_IMAGE_FIELD.file or anything you need to get the path to the image
You need to use Content-Disposition header, take a look here:
Generating file to download with Django
Django Serving a Download File
A class based views-type exampe of this would be like this (i'm using python-magic to get the correct content-type for the file):
import os
import magic
from django.views.generic import View
from django.http import HttpResponse
from .models import ImageModel
class ImageDownloadView(View):
def get(self, request, *args, **kwargs):
image = ImageModel.objects.get(pk=self.kwargs['image_id'])
image_buffer = open(image.file.path, "rb").read()
content_type = magic.from_buffer(image_buffer, mime=True)
response = HttpResponse(image_buffer, content_type=content_type);
response['Content-Disposition'] = 'attachment; filename="%s"' % os.path.basename(image.file.path)
return response
This works for Django 1.10.7 but shouldn't be that different for Django 1.7
The other two answers are ok, but as advertised in many places using Django to serve an static file is not recommended for performance reasons. It is better to serve it using your web server (nginx/apache...).
You don't need an extra view to serve static files. Simply render a link to the file in your template:
Download this image!
Where object is an instance of ImageModel.
See django.db.models.fields.files.FieldFile.url
If you really want to have a view in an URL like www.example.com/image/download/1 you can simply write a view that redirect to the image URL obtained from the field.
Here is your cross browser safe download for files containing any character type
# Even better as it works in any browser (mobile and desktop)
def safe_name(file_name):
"""
Generates a safe file name, even those containing characters like ? and &
And your Kanji and Cyrillics are supported!
"""
u_file_name = file_name.encode('utf-8')
s_file_name = re.sub('[\x00-\xFF]', lambda c: '%%%02x' % ord(c.group(0)), u_file_name)
return s_file_name
# Handled by url(r'^/image/download/(\d+)/.+$
def safe_download_image(request, image_id):
"""
Safely downloads the file because the filename is part of the URL
"""
img = ImageModel.objects.get(id=image_id)
wrapper = FileWrapper(open(img.file)) # img.file returns full path to the image
content_type = mimetypes.guess_type(filename)[0] # Use mimetypes to get file type
response = HttpResponse(wrapper,content_type=content_type)
response['Content-Length'] = os.path.getsize(img.file)
# This works for most browsers, but IE will complain sometimes
response['Content-Disposition'] = "attachment;"
return response
def download_image(request, image_id):
img = ImageModel.objects.get(id=image_id)
redirect_do = safe_name(img.name)
return HttpResponseRedirect('/image/download/' + img_id + '/' + redirect_to)
It's doesn't work. I did something like this:
wrapper = FileWrapper(img.file) # img.file returns full path to the image
content_type = mimetypes.guess_type(str(img.file))[0] # Use mimetypes to get file type
response = HttpResponse(wrapper, content_type=content_type)
response['Content-Length'] = os.path.getsize(str(img.file))
response['Content-Disposition'] = "attachment; filename=%s" % img.name
where img points to my ImageField field. File is downloaded, but I can't open it. xUbuntu image viewer seys 'Not a JPEG file. starts with 0x89 0x50'
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)
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