在保存前处理Django上传的图像

我在调查中发现,在Django上传图像后,我想在保存之前对其进行处理,但大多数情况下,人们通常是先保存,然后再重新打开再进行处理的模式。
借助AI的力量,我成功地实现了很好的处理效果,现在与大家分享。我认为这可能是已经有人研究过的内容,所以可能是二次创作。

版本我正在使用Python和Django的版本。(虽然无关紧要,但是Django经常有重大的版本更改,所以在发布时请务必写清楚版本。)
我在树莓派4B上的Docker容器中运行它。

# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

# python --version
Python 3.11.4

# python -m django --version
4.2.4

实施
我正在逐个上传文件,但最重要的是views.py。关于它的说明将在稍后提到。

    モデル
from django.db import models

class UploadImage(models.Model):
    # 画像
    image = models.ImageField(upload_to='img/')
    # 保存日時
    pub_date = models.DateTimeField('date published', auto_now_add=True)

    # 画像の名称を表示
    def __str__(self):
        return self.image.name
    画像投稿用の静的ファイル
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>{{title}}</title>
</head>
<body>
    <h1>{{title}}</h1>
    <div>
        {% if id is not None %}
        <h2>画像が登録されました</h2>
        <p>画像のIDは {{id}} です</p>
        {% endif %}
    </div>
    <div>
        <h2>画像を登録する</h2>
        <form action="{% url 'img_form' %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <p>{{ upload_form.as_p }}</p>
            <p><input type="submit" value="アップロード"></p>
        </form>
    </div>
</body>
</html>
    投稿用フォーム
from django import forms
from .models import UploadImage

class UploadForm(forms.ModelForm):
    class Meta:
        model = UploadImage
        fields = ('image', )
    投稿時の処理
from django.shortcuts import render, get_object_or_404
from .forms import UploadForm, DisplaySettingsForm
from .models import UploadImage
from mqttlib.message import HOST, PORT, DisplayImages

from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
from pathlib import Path

# 画像サイズ
IMG_SIZE: tuple[int, int] = (800, 480)

def upload_img_form(request):
    params = {
        'title': '画像のアップロード',
        'upload_form': UploadForm(),
        'id': None,
    }

    if (request.method == 'POST'):
        form: UploadForm = UploadForm(request.POST, request.FILES)
        if form.is_valid():
            # メモリ上のデータを読み込む
            mem_img: InMemoryUploadedFile = form.cleaned_data['image']
            # InMemoryUploadedFileのデータをBytesIOにコピー
            byte_io: BytesIO = BytesIO()
            for chunk in mem_img.chunks():
                byte_io.write(chunk)
            byte_io.seek(0)

            # BytesIOからPIL Imageを読み込む
            org_img: Image.Image = Image.open(byte_io)

            # 画像の加工
            tgt_img: Image.Image = org_img.resize(IMG_SIZE)
            image_io: BytesIO = BytesIO()
            tgt_img.save(image_io, format="BMP")
            bmp_path: str = str(Path(mem_img.name).with_suffix('.bmp'))

            # 画像の保存
            uploaded_file = InMemoryUploadedFile(
                image_io, None, bmp_path, mem_img.content_type,
                image_io.tell(), None
            )
            form.cleaned_data['image'] = uploaded_file
            form.instance.image = uploaded_file
            post = form.save()
            params['id'] = post.id
            pass
    return render(request, 'upload_app/img_form.html', params)

变量名可能稍微与内容不太相符,但请不要在意。
每个处理块都有注释,所以我认为您大致能够理解正在进行的处理内容。
要将其转换成日语,可以沿以下流程进行。

    1. 提取存储在内存中的POST数据中的InMemoryUploadedFile(此时仅存在于内存中)。

将其转换为PIL.Image,将其移动到BytesIO缓冲区,并用PIL.Image打开。

对图像数据进行处理。这里包括将其大小调整为IMG_SIZE并将文件内容转换为BMP格式。

从PIL.Image转换回InMemoryUploadedFile(与上述操作相反)。

将处理后的数据替换回原始数据并保存。

在最后一次替换中,也许只需要将加工后的数据uploaded_file赋值给form.instance.image,并不需要将其赋值给form.cleaned_data[‘image’],但我感觉变量的内容有点混乱,所以还是加上了。

可以参考
使用Django进行简单图像处理(使用PIL库)
使用Django和Pillow将注册图片调整到特定尺寸

bannerAds