Django图像在上传之前调整大小和转换

时间:2014-06-23 18:58:09

标签: python django django-models

我在这个问题上搜索了很多,但却找不到我需要的东西。我将解释我的问题:

在我的网站上,用户可以上传图片。我需要调整此图片的大小,然后在 上传之前将其转换为JPEG

我在Views.py中看到了一些使用方法的解决方案,但我想通过覆盖Models.py中的save()方法来实现。问题是,我发现在save方法中调整图像大小的所有解决方案都是在第一次保存后调用它(调用超级函数),这意味着它使用带宽(如果我使用CDN)什么都不做(我是对的)关于这一点?)。

感谢您的帮助

9 个答案:

答案 0 :(得分:21)

首先,建立正确的语言是最好的。 Django和Python仅存在于服务器端。因此,他们操纵,保存或以其他方式使用的任何东西都必须首先发送到服务器。如果Django或Python要管理照片,用户必须首先将此照片上传到服务器。上传照片后,Django可以在存储文件之前自由进行更改。

如果您担心上传带宽,并且您不希望上传大文件,则必须在客户端调整照片大小并重新格式化。如果这是一个Web应用程序,可以使用Javascript完成,但不能用Python完成,因为Python不能在客户端运行像你这样的应用程序。

如果您的担忧不在于带宽,那么您可以自由地让用户“上传”#34;该文件,但随后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的行。

我也发现了一个类似的问题here

修改:看到了上述评论回复的更新。另请注意,如果您的Web服务器正在处理Django,并且您的文件正在保存到某些CDN上的数据库中,则此方法将起作用。在上传到您的CDN之前,图像将在网络服务器上调整大小(假设您的配置正如我所假设的那样)。

希望这有帮助!

答案 1 :(得分:7)

这是一款可以解决这个问题的应用: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}))
    ])

答案 2 :(得分:5)

EDIT4:可以找到完整的工作代码here(下面的代码仍有一些错误)

仍在战斗,但有进步^^:我正在尝试用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())”

时遇到错误

由于

编辑:对不起,这是一个简单的错误(忘记了自己。)在代码中纠正。现在我在同一行有错误“TypeError,'unicode'对象不可调用”。

EDIT2:看到有关“output.getvalue()”here的内容,但不知道是否可以提供任何帮助。

EDIT3:解决了!!代码如下。

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)

For PNG looking weird, see second post on this thread

答案 3 :(得分:3)

为了我自己的项目把这个放在一起,在我意识到Django ImageField上的字节和图像是单独的属性之前花了我一段时间,这个解决方案适用于Python 3.6。

    def clean_image(self):
        image_field = self.cleaned_data.get('image')
        if image_field:
            try:
                image_file = BytesIO(image_field.file.read())
                image = Image.open(image_file)
                image.thumbnail((300, 300), Image.ANTIALIAS)
                image_file = BytesIO()
                image.save(image_file, 'PNG')
                image_field.file = image_file
                image_field.image = image

                return image_field
            except IOError:
                logger.exception("Error during resize image")

答案 4 :(得分:1)

这对我有用,请尝试一下。我正在使用Django 2.0.6和Python 3.6.4

from django.db import models
from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile

class ImageUpload(models.Model):
    name = models.CharField(max_length=100)
    uploadedImage = models.ImageField(upload_to='Uploads/%Y/%m/', db_column="Upload an Image")
    def save(self, *args, **kwargs):
        imageTemproary = Image.open(self.uploadedImage)
        outputIoStream = BytesIO()
        imageTemproaryResized = imageTemproary.resize( (1020,573) ) 
        imageTemproaryResized.save(outputIoStream , format='JPEG', quality=85)
        outputIoStream.seek(0)
        self.uploadedImage = InMemoryUploadedFile(outputIoStream,'ImageField', "%s.jpg" %self.uploadedImage.name.split('.')[0], 'image/jpeg', sys.getsizeof(outputIoStream), None)
        super(ImageUpload, self).save(*args, **kwargs)

答案 5 :(得分:1)

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

此示例显示了图像调整大小后如何上传图像。 根据需要更改new_img的像素。

答案 6 :(得分:0)

这里还有另外一个对我有效的软件包,只需最少的代码修改-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

就是这样!

答案 7 :(得分:0)

最简单的解决方案:

int main(void)
{
   int iArray[3] = {100,234,567};

   f1(iArray);
   f2(iArray);
   f3(iArray);

   return EXIT_SUCCESS;
}

void f1(int ia[3]) // sized array
{
   ia[0] = 1;
   ia[1] = 2;
   ia[2] = 3;

   for (int i = 0; i < 3; i++)
   {
      printf("%d\n", ia[i]);
   }
   printf("\n");
}

void f2(int* pi) // pointer
{
   *pi = 11, 12, 13;

   printf("%d", *pi);
   printf("\n\n");

}

void f3(int ia[]) // unsized array
{
   ia[0] = 101;
   ia[1] = 102;
   ia[2] = 103;

   for (int i = 0; i < 3; i++)
   {
      printf("%d\n", ia[i]);
   }
}

答案 8 :(得分:0)

from PIL import Image
class Article(TimeStampedModel):
image = models.ImageField(upload_to='article_images/', null=True, blank=True)


def save(self, *args, **kwargs):
    if self.image:
        super().save(*args, **kwargs)
        img = Image.open(self.image.path)
        if img.height > 700 or img.weight > 700:
            output_size = (700,700)
            img.thumbnail(output_size)
            img.save(self.image.path)