📘

Djangoで画像を圧縮してアップロードできるようにしたときの備忘録

2024/09/10に公開

EC2インスタンス上でWebアプリケーションを動かしてみると思ったよりかも画面の読み込みが重くなってしまいました。
原因として画像の容量が大きいことが考えられたので画像の容量を圧縮する方法を調べて実装しました。

DjangoAdminSiteの管理画面から画像をアップロードする際に画像を圧縮する方法を模索してみた結果、modelsクラスのsaveメソッドをオーバーライドすることにしました。

Pillow、ByteIO、Fileの3つを新しくimportしました。

from PIL import Image
from io import BytesIO
from django.core.files import File

以下のようなコードを実装しました。今回は画像登録時に拡張子を.jpg形式に変換して容量を圧縮してから保存しています。

class Art(models.Model):
    art = models.ImageField(upload_to='ファイル保存先')

    def save(self, *args, **kwargs):
        img = Image.open(self.art)
        img = img.convert('RGB')
        output = BytesIO()
        img.save(output, format='JPEG', quality=70)
        output.seek(0)
        self.art = File(output, self.art.name.split('.')[0] + '.jpg')
        super(Art, self).save(*args, **kwargs)

img = img.convert('RGB')画像をRGBモードに変換しています。PNGなどの透明度を持つ画像形式(RGBAやPモードの画像)の場合でもJPEG形式に変換できるように、モードをRGBに変換しています。

BytesIOクラスはメモリ上にバイナリデータ(バイト列)を扱うためのバッファ(仮想ファイル)を作成するためのものです。
メモリ上に一時的に画像データを保存して加工編集などの処理を加えることが出来ます。

output = BytesIO()
img.save(output, format='JPEG', quality=70)
output.seek(0)

1行目でBytesIOのバッファを作成して変数に格納しています。
2行目でJPEGとして画像をバッファに保存しています。
3行目でバッファの位置を先頭に戻しています。

self.art = File(output, self.art.name.split('.')[0] + '.jpg')でメモリ上の画像データをFileオブジェクトとして拡張子を.jpgに置き換えてから保存しています。

super(Art, self).save(*args, **kwargs)ではsuper()でArtクラスの親クラス(models.Model)を呼び出しています。selfでsaveメソッドの呼び出し対象のインスタンスを指定しています。

以上の方法でDjangoで画像を圧縮してアップロードしました。
しかしこの方法だと、画像の解像度が荒いものから細かいものまで一律で解像度を下げてしまいます。
そのため、もっとPillowについて調べて一定の解像度以上の画像のみに圧縮処理を適用できるように改善していこうと考えています。

Discussion