1
1
from django .utils .translation import gettext_lazy as _
2
2
from rest_framework import serializers
3
+ from nxtbn import settings
3
4
from nxtbn .filemanager .models import Image , Document
4
5
5
6
6
7
from PIL import Image as PILImage
7
8
from io import BytesIO
8
9
from django .core .files .base import ContentFile
9
10
11
+
10
12
class ImageSerializer (serializers .ModelSerializer ):
11
13
class Meta :
12
14
model = Image
13
15
fields = "__all__"
14
- read_only_fields = (
15
- "id" ,
16
- "created_by" ,
17
- "last_modified_by" ,
18
- )
16
+ read_only_fields = ("id" , "created_by" , "last_modified_by" )
19
17
20
18
def create (self , validated_data ):
21
19
request = self .context ["request" ]
@@ -25,6 +23,7 @@ def create(self, validated_data):
25
23
# Optimize the image before saving
26
24
if "image" in validated_data :
27
25
validated_data ["image" ] = self .optimize_image (validated_data ["image" ])
26
+ validated_data ["image_xs" ] = self .optimize_image (validated_data ["image" ], max_size_kb = 1 , format = "png" , max_dimension = 50 )
28
27
29
28
return super ().create (validated_data )
30
29
@@ -39,28 +38,38 @@ def update(self, instance, validated_data):
39
38
return super ().update (instance , validated_data )
40
39
41
40
@staticmethod
42
- def optimize_image (image_file , max_size_kb = 200 , format = "WEBP" ):
41
+ def optimize_image (image_file , max_size_kb = settings . IMAGE_COMPRESS_MAX , format = "WEBP" , max_dimension = 800 ):
43
42
"""
44
43
Optimize an image by resizing and converting it to the specified format while maintaining the aspect ratio.
45
44
Ensures the image file size is below max_size_kb.
46
45
"""
47
46
img = PILImage .open (image_file )
48
- img = img .convert ("RGB" ) # Ensure compatibility for formats like PNG with alpha
47
+ img = img .convert ("RGB" ) # Convert to RGB for compatibility
48
+
49
+ # Adjust dimension based on image type (smaller for PNG to ensure < 2023 bytes)
50
+ if format .lower () == "png" :
51
+ max_dimension = 64 # Reduce size for PNG to keep it small
49
52
50
- # Resize the image to fit within a reasonable dimension (adjust if needed)
51
- max_dimension = 800
52
53
img .thumbnail ((max_dimension , max_dimension ), PILImage .Resampling .LANCZOS )
53
54
54
- # Save the image to a buffer
55
+ # Use a buffer to store the optimized image
55
56
buffer = BytesIO ()
56
- img .save (buffer , format = format , optimize = True , quality = 85 )
57
- buffer .seek (0 )
58
57
59
- # Check if the image exceeds the maximum size, reduce quality iteratively
60
- while buffer .tell () > max_size_kb * 1024 :
61
- buffer .seek (0 )
62
- img .save (buffer , format = format , optimize = True , quality = max (10 , 85 - 10 ))
63
- buffer .seek (0 )
58
+ if format .lower () == "png" :
59
+ # Reduce colors using quantization (important for PNG)
60
+ img = img .convert ("P" , palette = PILImage .ADAPTIVE , colors = 32 ) # Limit colors to 32
61
+ img .save (buffer , format = "PNG" , optimize = True )
62
+ else :
63
+ quality = 85
64
+ img .save (buffer , format = format , optimize = True , quality = quality )
65
+
66
+ # Reduce quality iteratively for WebP/JPEG if size is too large
67
+ while buffer .tell () > max_size_kb * 1024 and quality > 10 :
68
+ quality -= 5
69
+ buffer = BytesIO () # Reset buffer
70
+ img .save (buffer , format = format , optimize = True , quality = quality )
71
+
72
+ buffer .seek (0 )
64
73
65
74
# Return the new image as a ContentFile
66
75
return ContentFile (buffer .read (), name = f"{ image_file .name .split ('.' )[0 ]} .{ format .lower ()} " )
0 commit comments