Unable to save webp file to model field - python

I'm not sure if this is entirely django related, but if someone could help me, that would be so much appreciated! I'm having trouble generating a webp file from the following code
from io import BytesIO
from PIL import Image
import requests
I've got the following model
class UserImage(models.Model):
user_provided_image = VersatileImageField(upload_to=folder10, null=True, blank=True)
nextgen_image = models.FileField(upload_to=folder10,null=True, blank=True) #for WebP images
I'm creating a webp file. This code works, but it saved it to the file to the root directory of my project and I'm not sure how to save it to the FileField (i.e. nextgen_image ) on my model
def create_webp_image(sender, instance, *args, **kwargs):
image_url = instance.image.thumbnail['1920x1080'].url
try:
response = requests.get(image_url, stream=True)
path = image_url
except: #local env
path = "http://localhost:8000" + image_url
response = requests.get(path, stream=True)
img = Image.open(BytesIO(response.content))
#build file path
position = path.rfind("/") + 1
newpath = path[0:position]
#build file name
image_name = path[position:]
name_of_file = image_name.split('.')[0] + ".webp"
#this creates the webp file
img.save(name_of_file,"webp")
#save image to model
#instance.nextgen_image = ?
post_save.connect(create_webp_image, sender=UserImage)
Thanks!

You can use something like that:
from django.core.files.base import ContentFile
...
img_content = ContentFile(BytesIO(response.content))
instance.nextgen_image.save(name_of_file, img_content, save=True)
...

If you want to use packages to get the job done then use this package django-resized.
Based on the code provided above this should do the trick. Hoping that this will solve your issue. Cheers
nextgen_image = ResizedImageField(force_format="WEBP",
upload_to=folder10,null=True, blank=True)

Related

Django convert image to webp

I have service in my Django project's app, that upload images, and I need to convert all images to webp to optimize further work with these files on the frontend side.
Draft of _convert_to_webp method:
# imports
from pathlib import Path
from django.core.files import temp as tempfile
from django.core.files.uploadedfile import InMemoryUploadedFile
from PIL import Image
# some service class
...
def _convert_to_webp(self, f_object: InMemoryUploadedFile):
new_file_name = str(Path(f_object._name).with_suffix('.webp'))
temp_file = tempfile.NamedTemporaryFile(suffix='.temp.webp')
# FIXME: on other OS may cause FileNotFoundError
with open(temp_file 'wb') as f:
for line in f_object.file.readlines():
... # will it works good?
new_file = ...
new_f_object = InMemoryUploadedFile(
new_file,
f_object.field_name,
new_file_name,
f_object.content_type,
f_object.size,
f_object.charset,
f_object.content_type_extra
)
return new_file_name, new_f_object
...
f_object is InMemoryUploadedFile instance from POST request body (Django automatically create it).
My idea is to create a temporary file, write data from f_object.file.readlines() to it, open this file with PIL.Image.open and save with format="webp". Is this idea a good one or there is another way to make file converting?
I found a pretty clean way to do this using the django-resized package.
After pip installing, I just needed to swap out the imageField for a ResizedImageField
img = ResizedImageField(force_format="WEBP", quality=75, upload_to="post_imgs/")
All image uploads are automatically converted to .webp!
The solution was pretty simple. PIL.Image can be opened using file instance, so I just opened it using f_object.file and then saved it in BytesIO instance with optimization and compression.
Correctly working code:
# imports
from pathlib import Path
from django.core.files.uploadedfile import InMemoryUploadedFile
from PIL import Image
# some service class
...
def _convert_to_webp(self, f_object: InMemoryUploadedFile):
suffix = Path(f_object._name).suffix
if suffix == ".webp":
return f_object._name, f_object
new_file_name = str(Path(f_object._name).with_suffix('.webp'))
image = Image.open(f_object.file)
thumb_io = io.BytesIO()
image.save(thumb_io, 'webp', optimize=True, quality=95)
new_f_object = InMemoryUploadedFile(
thumb_io,
f_object.field_name,
new_file_name,
f_object.content_type,
f_object.size,
f_object.charset,
f_object.content_type_extra
)
return new_file_name, new_f_object
95% was chosen as balanced parameter. There was very bad quality with quality=80 or quality=90.

Django - edit uploaded file Before final saving

i'm making a site for uploading and processing of files (mostly images but also other stuff). sometimes, some files need some editing before saving.
i want edit the uploaded file before saving it to media dir (mostly to avoid IO interaction and only keep original file in memory and discard after saving edit version).
edit: so i thought i have to run the edit function in POST, and change information in request.FILES. but i failed, and don't know what to do anymore.
NOTE: i'm mostly looking for a way to do it via class views.
here's some code for reference:
The model:
class FilePatient(models.Model):
file_imag = models.FileField(upload_to='')
The View:
class FileAddView(LoginRequiredMixin, UserPassesTestMixin, SuccessMessageMixin, CreateView):
model = FilePatient
fields = ['file_imag']
def post(self, request, *args, **kwargs):
if request.FILES['file_imag'].name.endswith('.png'):
newFile=editFile(request.FILES['image_imag'])
# what to do, what not to do
return super().post(request, *args, **kwargs)
I hope the following will help you... this code "edits" the file by resizing the original file if it is too big. Based on working code, I substituted the vars you used in your question, not tested as below.
from pathlib import Path
import PIL
from six import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
img = request.FILES['fileInput']
if img.name.endswith('.png'):
fp = FilePatient.objects.get(...) # get the object for uploading file
img2 = PIL.Image.open(img)
exif = img2.info['exif'] if 'exif' in img2.info else None
max_hw = 800 # px width / height maximum
width_percent = (max_hw/float(img2.size[0]))
heigth_percent = (max_hw/float(img2.size[1]))
min_wh_percent = float(min(width_percent, heigth_percent))
if min_wh_percent <= 1:
width_size = int((float(img2.size[0])*min_wh_percent))
height_size = int((float(img2.size[1])*min_wh_percent))
img2 = img2.resize((width_size, height_size), PIL.Image.ANTIALIAS)
fp.file_imag = img2
buffer = BytesIO()
if exif:
img2.save(buffer, format='JPEG', exif=exif, quality=90)
else:
img2.save(buffer, format='JPEG', quality=90)
buffer.seek(0)
fp.file_imag.name = Path(img.name).stem
fp.file_imag = InMemoryUploadedFile(buffer,
'ImageField',
f"{fp.file_imag.name}.jpg",
'image/jpeg',
img2.size,
"utf-8")
fp.save()

How to generate barcode in python as response of image object

Views.py:
def Bar(request):
payload = json.loads(request.body.decode('utf-8'))
a=payload["donor_n_key"]
ean = barcode.get('code128', a, writer=ImageWriter())
filename = ean.save('ean13')
image = ean.render()
return HttpResponse(image,content_type="image/png")
Here i have downloaded the barcode image but I am unable to open that image.I am getting the error is windows photo viewer can't open this picture because either photo viewer does not support this file format.
I am new to this django restframework.Please help me Anyone.
In Your View.py
Import This
import barcode
from barcode.writer import ImageWriter
def Bar(request):
lineCode = '1234567891234'
barCodeImage = barcode.get('ean13', lineCode, writer=ImageWriter())
filename = barCodeImage.save(item_name.png)
return HttpResponse(image,content_type="image/png")
It Will Generates Image and save it your project root folder
More help visit this link PyBarcode

Django save base64 string to filesystem using models.ImageField

I am trying to upload image to file system using python django. I dont have any idea on how to proceed further.
in my model.py:
Class Test(object):
mission = models.TextField(blank=True)
image = models.ImageField(upload_to='documents/images/',blank=True)
account = models.OneToOneField(Account, related_name='account')
in my view.py
def add_image(request, account):
req = get_request_json(request)
data = Test.objects.add_image(account, req)
in my manager.py
Class TestManager(models.Manager):
def add_image(self, account, input):
acc = self.get(account=account)
acc.save()
return acc;
But I am not sure how to proceed from here.
I would like to know how to save the base64 image string to the specified location and store the path/file name in database?
I have worked with python where I write the files to a directory and get the path and store in db. Here I want to use the django options.
I have to repeat the same process with other file formats too.
If you have an image in base64 string format and you want to save it to a models ImageField, this is what I would do
import base64
from django.core.files.base import ContentFile
image_b64 = request.POST.get('image') # This is your base64 string image
format, imgstr = image_b64.split(';base64,')
ext = format.split('/')[-1]
data = ContentFile(base64.b64decode(imgstr), name='temp.' + ext)
Now, you can simply do
Test.objects.create(image=data)

Download a remote image and save it to a Django model

I am writing a Django app which will fetch all images of particular URL and save them in the database.
But I am not getting on how to use ImageField in Django.
Settings.py
MEDIA_ROOT = os.path.join(PWD, "../downloads/")
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash.
# Examples: "http://example.com/media/", "htp://media.example.com/"
MEDIA_URL = '/downloads/'
models.py
class images_data(models.Model):
image_id =models.IntegerField()
source_id = models.IntegerField()
image=models.ImageField(upload_to='images',null=True, blank=True)
text_ind=models.NullBooleanField()
prob=models.FloatField()
download_img.py
def spider(site):
PWD = os.path.dirname(os.path.realpath(__file__ ))
#site="http://en.wikipedia.org/wiki/Pune"
hdr= {'User-Agent': 'Mozilla/5.0'}
outfolder=os.path.join(PWD, "../downloads")
#outfolder="/home/mayank/Desktop/dreamport/downloads"
print "MAYANK:"+outfolder
req = urllib2.Request(site,headers=hdr)
page = urllib2.urlopen(req)
soup =bs(page)
tag_image=soup.findAll("img")
count=1;
for image in tag_image:
print "Image: %(src)s" % image
filename = image["src"].split("/")[-1]
outpath = os.path.join(outfolder, filename)
urlretrieve('http:'+image["src"], outpath)
im = img(image_id=count,source_id=1,image=outpath,text_ind=None,prob=0)
im.save()
count=count+1
I am calling download_imgs.py inside one view like
if form.is_valid():
url = form.cleaned_data['url']
spider(url)
Django Documentation is always good place to start
class ModelWithImage(models.Model):
image = models.ImageField(
upload_to='images',
)
UPDATED
So this script works.
Loop over images to download
Download image
Save to temp file
Apply to model
Save model
.
import requests
import tempfile
from django.core import files
# List of images to download
image_urls = [
'http://i.thegrindstone.com/wp-content/uploads/2013/01/how-to-get-awesome-back.jpg',
]
for image_url in image_urls:
# Stream the image from the url
response = requests.get(image_url, stream=True)
# Was the request OK?
if response.status_code != requests.codes.ok:
# Nope, error handling, skip file etc etc etc
continue
# Get the filename from the url, used for saving later
file_name = image_url.split('/')[-1]
# Create a temporary file
lf = tempfile.NamedTemporaryFile()
# Read the streamed image in sections
for block in response.iter_content(1024 * 8):
# If no more file then stop
if not block:
break
# Write image block to temporary file
lf.write(block)
# Create the model you want to save the image to
image = Image()
# Save the temporary image to the model#
# This saves the model so be sure that it is valid
image.image.save(file_name, files.File(lf))
Some reference links:
requests - "HTTP for Humans", I prefer this to urllib2
tempfile - Save temporay file and not to disk
Django filefield save
If you want to save downloaded images without saving them to disk first (without using NamedTemporaryFile etc) then there's an easy way to do that.
This will be slightly quicker than downloading the file and writing it to disk as it is all done in memory. Note that this example is written for Python 3 - the process is similar in Python 2 but slightly different.
from django.core import files
from io import BytesIO
import requests
url = "https://example.com/image.jpg"
resp = requests.get(url)
if resp.status_code != requests.codes.ok:
# Error handling here
fp = BytesIO()
fp.write(resp.content)
file_name = url.split("/")[-1] # There's probably a better way of doing this but this is just a quick example
your_model.image_field.save(file_name, files.File(fp))
Where your_model is an instance of the model you'd like to save to and .image_field is the name of the ImageField.
See the documentation for io for more info.
# this is my solution
from django.core import files
from django.core.files.base import ContentFile
import requests
from .models import MyModel
def download_img():
r = requests.get("remote_file_url", allow_redirects=True)
filename = "remote_file_url".split("/")[-1]
my_model = MyModel(
file=files.File(ContentFile(r.content), filename)
)
my_model.save()
return
As an example of what I think you're asking:
In forms.py:
imgfile = forms.ImageField(label = 'Choose your image', help_text = 'The image should be cool.')
In models.py:
imgfile = models.ImageField(upload_to='images/%m/%d')
So there will be a POST request from the user (when the user completes the form). That request will contain basically a dictionary of data. The dictionary holds the submitted files. To focus the request on the file from the field (in our case, an ImageField), you would use:
request.FILES['imgfield']
You would use that when you construct the model object (instantiating your model class):
newPic = ImageModel(imgfile = request.FILES['imgfile'])
To save that the simple way, you'd just use the save() method bestowed upon your object (because Django is that awesome):
if form.is_valid():
newPic = Pic(imgfile = request.FILES['imgfile'])
newPic.save()
Your image will be stored, by default, to the directory you indicate for MEDIA_ROOT in settings.py.
Accessing the image in the template:
<img src="{{ MEDIA_URL }}{{ image.imgfile.name }}"></img>
The urls can be tricky, but here's a basic example of a simple url pattern to call the stored images:
urlpatterns += patterns('',
url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {
'document_root': settings.MEDIA_ROOT,
}),
)
I hope it helps.
Similar to #boltsfrombluesky's answer above you can do this in Python 3 without any external dependencies like so:
from os.path import basename
import urllib.request
from urllib.parse import urlparse
import tempfile
from django.core.files.base import File
def handle_upload_url_file(url, obj):
img_temp = tempfile.NamedTemporaryFile(delete=True)
req = urllib.request.Request(
url, data=None,
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'
}
)
with urllib.request.urlopen(req) as response:
img_temp.write(response.read())
img_temp.flush()
filename = basename(urlparse(url).path)
result = obj.image.save(filename, File(img_temp))
img_temp.close()
return result
Try doing it this way instead of assigning path to the image...
import urllib2
from django.core.files.temp import NamedTemporaryFile
def handle_upload_url_file(url):
img_temp = NamedTemporaryFile()
opener = urllib2.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0) Gecko/20120427 Firefox/15.0a1')]
img_temp.write(opener.open(url).read())
img_temp.flush()
return img_temp
use the above function like this..
new_image = images_data()
#rest of the data in new_image and then do this.
new_image.image.save(slug_filename,File(handle_upload_url_file(url)))
#here slug_filename is just filename that you want to save the file with.
In case you are saving image by overriding models' save method to modify the name of file and struggling with random invalid filename(like me) in django. You can follow up below code (Copied from Accepted answer):
lf = tempfile.NamedTemporaryFile()
for block in response.iter_content(1024*8):
if not block:
break
lf.write(block)
lf.name = name. # Set your custom file name here
dc = ImageFile(file=files.File(lf))
dc.file.save()
I have configured my storage with django-storages, in order to directly upload media content to s3. For some reasons I wasn't able to replace file name. After some R&D it worked.
Note: I have used FileField in the model, hence few line of code is not needed
def qrcodesave(request):
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

Categories

Resources