压缩图片到指定大小以内 Args: image_data: 原始图片数据 max_size_kb: 最大文件大小(KB) quality_start: 起始压缩质量(1-100) quality_min: 最低压缩质量(1-100) max_dimension: 最大边长(像素) Returns: 压缩后的图片数据
(
image_data: bytes,
max_size_kb: int = 200, # 默认200KB
quality_start: int = 85,
quality_min: int = 20,
max_dimension: int = 2048
)
| 5 | |
| 6 | |
| 7 | def compress_image( |
| 8 | image_data: bytes, |
| 9 | max_size_kb: int = 200, # 默认200KB |
| 10 | quality_start: int = 85, |
| 11 | quality_min: int = 20, |
| 12 | max_dimension: int = 2048 |
| 13 | ) -> bytes: |
| 14 | """ |
| 15 | 压缩图片到指定大小以内 |
| 16 | |
| 17 | Args: |
| 18 | image_data: 原始图片数据 |
| 19 | max_size_kb: 最大文件大小(KB) |
| 20 | quality_start: 起始压缩质量(1-100) |
| 21 | quality_min: 最低压缩质量(1-100) |
| 22 | max_dimension: 最大边长(像素) |
| 23 | |
| 24 | Returns: |
| 25 | 压缩后的图片数据 |
| 26 | """ |
| 27 | max_size_bytes = max_size_kb * 1024 |
| 28 | |
| 29 | # 如果原图已经小于目标大小,直接返回 |
| 30 | if len(image_data) <= max_size_bytes: |
| 31 | return image_data |
| 32 | |
| 33 | try: |
| 34 | # 打开图片 |
| 35 | img = Image.open(io.BytesIO(image_data)) |
| 36 | |
| 37 | # 转换为 RGB(处理 RGBA 等格式) |
| 38 | if img.mode in ('RGBA', 'LA', 'P'): |
| 39 | background = Image.new('RGB', img.size, (255, 255, 255)) |
| 40 | if img.mode == 'P': |
| 41 | img = img.convert('RGBA') |
| 42 | background.paste(img, mask=img.split()[-1] if img.mode in ('RGBA', 'LA') else None) |
| 43 | img = background |
| 44 | elif img.mode != 'RGB': |
| 45 | img = img.convert('RGB') |
| 46 | |
| 47 | # 如果图片尺寸过大,先缩小 |
| 48 | width, height = img.size |
| 49 | if width > max_dimension or height > max_dimension: |
| 50 | ratio = min(max_dimension / width, max_dimension / height) |
| 51 | new_width = int(width * ratio) |
| 52 | new_height = int(height * ratio) |
| 53 | img = img.resize((new_width, new_height), Image.Resampling.LANCZOS) |
| 54 | |
| 55 | # 逐步降低质量直到满足大小要求 |
| 56 | quality = quality_start |
| 57 | compressed_data = None |
| 58 | |
| 59 | while quality >= quality_min: |
| 60 | output = io.BytesIO() |
| 61 | img.save(output, format='JPEG', quality=quality, optimize=True) |
| 62 | compressed_data = output.getvalue() |
| 63 | |
| 64 | if len(compressed_data) <= max_size_bytes: |
no outgoing calls
no test coverage detected