如何在Django REST Framework中用Pillow压缩用户上传图片
Hey there! Let's walk through how to add automatic image compression to your Django REST Framework project using Pillow, specifically modifying your existing Photo model to handle this whenever users upload images.
Prerequisite: Install Pillow
First, make sure you have Pillow installed—if not, run this command in your virtual environment:
pip install pillow
Approach: Override the Model's save() Method
The cleanest way to handle image compression is to override your Photo model's save() method. This ensures compression runs every time an image is uploaded or updated, whether it's via the DRF API, admin panel, or any other part of your app.
Here's your modified Photo model with compression logic:
from PIL import Image from django.conf import settings from django.db import models class Photo(models.Model): title = models.CharField(max_length=100) uploader = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True) image_url = models.ImageField(upload_to='images', null=True) def save(self, *args, **kwargs): # Track if we're dealing with a new image or an updated one is_new_image = self.pk is None old_image_path = None if not is_new_image: # Grab the original image path before saving changes original_photo = Photo.objects.get(pk=self.pk) old_image_path = original_photo.image_url.path if original_photo.image_url else None # Save the model first to ensure the image is stored on disk super().save(*args, **kwargs) # Only compress if the image was added or changed if self.image_url and (is_new_image or self.image_url.path != old_image_path): try: # Open the uploaded image file with Image.open(self.image_url.path) as img: # Define your compression preferences max_dimension = 1200 # Max width/height (adjust to your needs) quality = 70 # JPEG quality (1-100; lower = smaller file size) # Resize the image while preserving aspect ratio if img.width > max_dimension or img.height > max_dimension: img.thumbnail((max_dimension, max_dimension), Image.Resampling.LANCZOS) # LANCZOS is a high-quality resampling filter for crisp resizes # Save the compressed image back to the same path img.save( self.image_url.path, optimize=True, # Removes unnecessary metadata quality=quality, format=img.format # Keep the original image format ) except Exception as e: # Handle errors (e.g., corrupted image files) without breaking the save print(f"Error compressing image: {str(e)}")
Key Details Explained:
- Avoid Re-Compression: We only process images when they're new or have been updated—this prevents re-compressing the same image every time you edit the
titleor other fields. - Smart Resizing: The
thumbnail()method maintains the image's aspect ratio while shrinking it to fit within yourmax_dimensionlimits, using a high-quality filter to avoid pixelation. - Quality Control: The
qualityparameter adjusts JPEG compression balance (lower values mean smaller files, slightly reduced quality).optimize=Truestrips extra metadata to further cut file size. - Error Safety: Wrapping the image processing in a
try-exceptblock ensures the model still saves even if something goes wrong with the image (like a corrupted file).
How It Works with DRF
When a user uploads an image via your DRF API, the serializer will create/update a Photo instance. The overridden save() method automatically triggers compression right after the image is saved to your filesystem. No extra changes are needed in your serializers or views—this logic lives entirely at the model level.
Optional Enhancements
- Convert to WebP: For even better compression, change the
formatparameter to'WEBP'inimg.save()(WebP files are 25-35% smaller than JPEGs with similar quality). - Per-User Settings: Add a model field to let users choose compression quality, or set different rules for different image types.
- Async Processing: For large images, offload compression to a background task (using Celery, for example) to avoid slowing down API responses.
内容的提问来源于stack exchange,提问作者Naterz

