Django上传前图像调整大小和转换
我在这个话题上搜索了很多,但还是找不到我需要的东西。让我来解释一下我的问题:
在我的网站上,用户可以上传一张图片。我需要在上传之前,把这张图片调整大小并转换成JPEG格式在上传之前。
我看到一些解决方案是使用Views.py中的方法,但我想通过重写Models.py中的save()方法来实现。问题是,我找到的所有在save方法中调整图片大小的解决方案都是在第一次保存之后进行的(调用super函数),这意味着如果我使用CDN的话,会浪费带宽(我这样理解对吗?)。
谢谢你的帮助。
10 个回答
编辑4:完整的工作代码可以在这里找到(下面的代码还有一些错误)
我还在努力,但有一些进展^^:我正在尝试用PIL在内存中进行调整大小和转换(看到这个主题上有很多问题,看来我不是唯一一个遇到这个问题的人^^)。到目前为止我的代码(在Models.py中):
from PIL import Image as Img
import StringIO
from django.core.files import File
class Mymodel(models.Model):
#blablabla
photo = models.ImageField(uppload_to="...", blank=True)
def save(self, *args, **kwargs):
if self.photo:
image = Img.open(StringIO.StringIO(self.photo.read()))
image.thumbnail((100,100), Img.ANTIALIAS)
output = StringIO.StringIO()
image.save(output, format='JPEG', quality=75)
output.seek(0)
self.photo = File(output, self.photo.name())
super(Mymodel, self).save(*args, **kwargs)
我在“photo = File(output, photo.name())”这一行遇到了错误
谢谢
编辑:抱歉,这是一个简单的错误(忘记加self.),已经在代码中修正了。现在我在同一行遇到了“TypeError,'unicode'对象不可调用”的错误。
编辑2:看到关于“output.getvalue()”的内容在这里,但我不太确定这是否有帮助。
编辑3:解决了!!下面是代码。
from PIL import Image as Img
import StringIO
from django.core.files.uploadedfile import InMemoryUploadedFile
class Mymodel(models.Model):
photo = models.ImageField(upload_to="...", blank=True)
def save(self, *args, **kwargs):
if self.photo:
image = Img.open(StringIO.StringIO(self.photo.read()))
image.thumbnail((200,200), Img.ANTIALIAS)
output = StringIO.StringIO()
image.save(output, format='JPEG', quality=75)
output.seek(0)
self.photo= InMemoryUploadedFile(output,'ImageField', "%s.jpg" %self.photo.name, 'image/jpeg', output.len, None)
super(Mymodel, self).save(*args, **kwargs)
这里有一个可以解决这个问题的应用:django-smartfields。它还会在上传新图片时自动删除旧的图片。
from django.db import models
from smartfields import fields
from smartfields.dependencies import FileDependency
from smartfields.processors import ImageProcessor
class ImageModel(models.Model):
image = fields.ImageField(dependencies=[
FileDependency(processor=ImageProcessor(
format='JPEG', scale={'max_width': 300, 'max_height': 300}))
])
这里还有一个对我来说很好用的包,几乎不需要修改代码 - django-resized。
这是 models.py 文件
from django_resized import ResizedImageField
class Post(models.Model):
image = ResizedImageField(upload_to='uploads/%Y/%m/%d')
这是 settings.py 文件
DJANGORESIZED_DEFAULT_SIZE = [1024, 768]
DJANGORESIZED_DEFAULT_QUALITY = 75
DJANGORESIZED_DEFAULT_KEEP_META = True
DJANGORESIZED_DEFAULT_FORCE_FORMAT = 'JPEG'
DJANGORESIZED_DEFAULT_FORMAT_EXTENSIONS = {'JPEG': ".jpg"}
DJANGORESIZED_DEFAULT_NORMALIZE_ROTATION = True
就这样!
from django.db import models
from django.contrib.auth.models import User
from PIL import Image
class profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.CharField(max_length=300)
location = models.CharField(max_length=99)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def save(self):
super().save() # saving image first
img = Image.open(self.image.path) # Open image using self
if img.height > 300 or img.width > 300:
new_img = (300, 300)
img.thumbnail(new_img)
img.save(self.image.path) # saving image at the same path
这个例子展示了如何在调整图片大小后上传图片。你可以根据自己的需要,随意修改新图片的像素大小。
首先,咱们得搞清楚用什么语言。Django和Python只在服务器上工作。所以,它们处理、保存或者使用的任何东西,都必须先发送到服务器。如果Django或Python要管理照片,用户必须先把照片上传到服务器。一旦照片上传完毕,Django就可以对它进行修改,然后再保存这个文件。
如果你担心上传的带宽,不想让大文件被上传,那你就得在客户端先调整照片的大小和格式。如果这是一个网页应用,可以用JavaScript来完成这个操作,但Python不能在客户端执行,因为对于你这样的应用,Python只在服务器上工作。
如果你不担心带宽问题,那用户可以直接“上传”文件,之后让Django在保存之前调整大小和格式。
你说得对,你需要重写照片对象的保存功能。我建议使用一个库来处理调整大小和格式的问题,比如sorl。
from sorl.thumbnail import ImageField, get_thumbnail
class MyPhoto(models.Model):
image = ImageField()
def save(self, *args, **kwargs):
if self.image:
self.image = get_thumbnail(self.image, '500x600', quality=99, format='JPEG')
super(MyPhoto, self).save(*args, **kwargs)
Sorl是我比较熟悉的一个库,但它需要一些调试和配置。你也可以看看Pillow之类的库,只需替换掉重写的self.image
那一行就行。
我还发现了一个类似的问题在这里。
补充:我看到你上面评论的更新了。如果你的网页服务器在处理Django,并且你的文件是保存到某个CDN上,这种方法是可行的。图片会在上传到CDN之前在网页服务器上被调整大小(假设你的配置和我想的差不多)。
希望这对你有帮助!