Django: 从表单中上传PIL图片存在问题

3

Django: 图像上传问题

Python: 3.5

Django: 1.11

我正在编写一个简单的新闻应用程序,其中将图像作为文章的一部分。我选择使用Django Forms提供的clean_(字段名称)函数修改图像并创建缩略图以存储在表单中。

我遇到的问题是,经过提交和clean_image函数后,ImageField验证将错误地指出“不允许文件扩展名”。

我知道问题来自于clean_image函数,在保存PIL图像后将其交还给Django ContentFile进行处理。任何帮助都将是巨大的。

表单:

from django.conf import settings
from django.core.files.base import ContentFile
from django.utils.translation import ugettext_lazy as _
from .models import News
from ironcsd.validators import MimetypeValidator
from datetime import date
from PIL import Image, ImageEnhance
from io import StringIO, BytesIO
import hashlib, mimetypes
import os, io
import logging, logging.config

logger = logging.getLogger(__name__)

class NewsForm(forms.ModelForm):

    class Meta:
        model = News
        widgets = {
            'details': forms.Textarea(attrs={'id': 'smde-editor'}),
        }
        fields = ('title','details','image')


    def clean_image(self):
        image = self.cleaned_data.get('image')
        md5 = hashlib.md5()
        md5.update(repr(image.name).encode('utf-8'))
        file_name = md5.hexdigest()

        if image._size > 30 * 1024 * 1024:
            raise forms.ValidationError(_('File is too big.'), code='invalid')

        image = Image.open(image)

        if image.size[0] < 1024 or image.size[1] < 1024:
            raise forms.ValidationError(_('Your image needs to be at least 1024x1024.'))

        image.thumbnail([1024,1024], Image.ANTIALIAS)
        image_string = io.BytesIO()
        image.save(image_string, image.format)
        image = ContentFile(image_string.getvalue(), file_name)

        return image

模型:

from django.db import models
from django.urls import reverse, reverse_lazy
from ironcsd.models import Audit

class News(Audit):
    title = models.CharField(max_length=255)
    details = models.TextField()
    image = models.ImageField(upload_to='news/%Y/%m/')

    class Meta:
        verbose_name_plural = "news"

    def __str__(self):
        return self.title

删除cleaned_data并尝试正常获取图像,查看错误是否仍然存在。 - Exprator
1个回答

2

我找到了问题所在。我的猜测是正确的,ContentFile 是有问题的。在 ContentFile 中,你需要提供 io 值和文件名。

ContentFile(image_io.getvalue(), image_name)

在image_name中,您需要确保图像具有文件扩展名(bmp、png、gif等),以便在转换回Django对象时,文件可以通过适当的验证。因此,代码答案如下。
我从已清理的图像名称创建了一个散列文件名。然后,我创建了一个名为image_name的变量,它将file_name作为基础,并添加了PIL图像格式作为扩展名。
image_name = '{}.{}'.format(file_name,image.format)

然后我在我的ContentFile中使用了这个image_name,将一个正确的文件传递给我的视图中的form_valid。

完整代码:

class NewsForm(forms.ModelForm):

    class Meta:
        model = News
        widgets = {
            'details': forms.Textarea(attrs={'id': 'smde-editor'}),
        }
        fields = ('title','details','image')


    def clean_image(self):
        image = self.cleaned_data.get('image')
        md5 = hashlib.md5()
        md5.update(repr(image.name).encode('utf-8'))
        file_name = md5.hexdigest()

        if image._size > 30 * 1024 * 1024:
            raise forms.ValidationError(_('File is too big.'), code='invalid')

        image = Image.open(image)

        if image.size[0] < 1024 or image.size[1] < 1024:
            raise forms.ValidationError(_('Your image needs to be at least 1024x1024.'))

        if image.format not in ('BMP','PNG','JPEG','GIF'):
            raise forms.ValidationError(_("Unsupported image type. Please uplod a bmp, png, jpeg, or gif."), code='invalid')

        image.thumbnail([1024,1024], Image.ANTIALIAS)
        image_io = io.BytesIO()
        image.save(image_io, format=image.format)
        image_name = '{}.{}'.format(file_name,image.format)
        image = ContentFile(image_io.getvalue(), image_name)

        return image

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接