Files
admin aac9f5934d feat: 添加多个功能模块和工具脚本
- 新增websocket客户端和服务端实现
- 添加图片压缩工具和快速压缩脚本
- 实现学生信息处理相关API
- 添加MQTT客户端和消息处理功能
- 更新.gitignore忽略更多文件类型
- 添加数据库操作工具和示例
- 实现多个测试脚本和工具类
2026-04-13 14:47:50 +08:00

234 lines
8.6 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
JPG照片压缩脚本
自动压缩超过3MB的JPG格式照片到小于3MB
"""
import os
from PIL import Image
import argparse
from pathlib import Path
def compress_jpg_to_size(file_path, max_size_mb=1, quality=85, step=5):
"""
压缩JPG图片到指定大小以下
Args:
file_path: 图片文件路径
max_size_mb: 最大文件大小(MB)
quality: 初始压缩质量(1-95)
step: 每次降低质量的步长
Returns:
bool: 是否成功压缩
str: 结果信息
"""
try:
# 获取文件大小
original_size = os.path.getsize(file_path)
original_size_mb = original_size / (1024 * 1024)
print(f"原始文件: {os.path.basename(file_path)}")
print(f"原始大小: {original_size_mb:.2f}MB")
# 如果文件已经小于目标大小,直接返回
if original_size_mb <= max_size_mb:
return True, f"文件已小于{max_size_mb}MB,无需压缩"
# 打开图片
with Image.open(file_path) as img:
# 确保是RGB模式
if img.mode != 'RGB':
img = img.convert('RGB')
# 获取图片尺寸
width, height = img.size
print(f"图片尺寸: {width}x{height}")
# 如果图片尺寸很大,先调整尺寸
max_dimension = 4000 # 最大边长限制
if width > max_dimension or height > max_dimension:
if width > height:
new_width = max_dimension
new_height = int(height * max_dimension / width)
else:
new_height = max_dimension
new_width = int(width * max_dimension / height)
img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
print(f"调整尺寸至: {new_width}x{new_height}")
# 尝试不同质量等级进行压缩
current_quality = quality
while current_quality >= 20: # 最低质量限制
# 保存为临时文件来检查大小
temp_output = f"{file_path}.temp.jpg"
img.save(temp_output, format='JPEG', quality=current_quality, optimize=True)
# 检查压缩后的大小
compressed_size = os.path.getsize(temp_output)
compressed_size_mb = compressed_size / (1024 * 1024)
print(f"尝试质量{current_quality}%: {compressed_size_mb:.2f}MB")
if compressed_size_mb <= max_size_mb:
# 压缩成功,替换原文件
os.replace(temp_output, file_path)
compression_ratio = (original_size - compressed_size) / original_size * 100
return True, f"压缩成功!质量:{current_quality}%, 大小:{compressed_size_mb:.2f}MB, 压缩率:{compression_ratio:.1f}%"
else:
# 删除临时文件,继续尝试更低质量
os.remove(temp_output)
current_quality -= step
# 如果还是太大,尝试进一步调整尺寸
print("质量压缩不足,尝试调整尺寸...")
# 逐步减小尺寸
scale_factor = 0.9
current_quality = quality # 重置质量
while scale_factor >= 0.5: # 最小尺寸限制
new_width = int(width * scale_factor)
new_height = int(height * scale_factor)
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
temp_output = f"{file_path}.temp.jpg"
resized_img.save(temp_output, format='JPEG', quality=current_quality, optimize=True)
compressed_size = os.path.getsize(temp_output)
compressed_size_mb = compressed_size / (1024 * 1024)
print(f"尝试尺寸{new_width}x{new_height} + 质量{current_quality}%: {compressed_size_mb:.2f}MB")
if compressed_size_mb <= max_size_mb:
os.replace(temp_output, file_path)
compression_ratio = (original_size - compressed_size) / original_size * 100
return True, f"压缩成功!尺寸:{new_width}x{new_height}, 质量:{current_quality}%, 大小:{compressed_size_mb:.2f}MB, 压缩率:{compression_ratio:.1f}%"
else:
os.remove(temp_output)
scale_factor -= 0.1
return False, "无法压缩到目标大小,图片可能过于复杂"
except Exception as e:
return False, f"压缩失败: {str(e)}"
def compress_single_file(file_path, max_size_mb=1):
"""
压缩单个文件
"""
# 检查文件是否存在
if not os.path.exists(file_path):
print(f"文件不存在: {file_path}")
return False
# 检查文件扩展名
if not file_path.lower().endswith(('.jpg', '.jpeg')):
print(f"不是JPG格式文件: {file_path}")
return False
success, message = compress_jpg_to_size(file_path, max_size_mb)
print(message)
return success
def compress_directory(directory_path, max_size_mb=1, recursive=True):
"""
压缩目录中的所有JPG文件
"""
if not os.path.exists(directory_path):
print(f"目录不存在: {directory_path}")
return
success_count = 0
failed_count = 0
skipped_count = 0
# 获取所有JPG文件
jpg_files = []
if recursive:
for root, dirs, files in os.walk(directory_path):
for file in files:
if file.lower().endswith(('.jpg', '.jpeg')):
jpg_files.append(os.path.join(root, file))
else:
for file in os.listdir(directory_path):
if file.lower().endswith(('.jpg', '.jpeg')):
full_path = os.path.join(directory_path, file)
if os.path.isfile(full_path):
jpg_files.append(full_path)
print(f"找到 {len(jpg_files)} 个JPG文件")
print("-" * 50)
# 处理每个文件
for i, file_path in enumerate(jpg_files, 1):
print(f"\\n[{i}/{len(jpg_files)}] 处理: {os.path.basename(file_path)}")
# 检查文件大小
file_size_mb = os.path.getsize(file_path) / (1024 * 1024)
if file_size_mb <= max_size_mb:
print(f"文件已小于{max_size_mb}MB,跳过")
skipped_count += 1
continue
success, message = compress_jpg_to_size(file_path, max_size_mb)
if success:
success_count += 1
else:
failed_count += 1
print(f"失败: {message}")
# 打印统计结果
print("\\n" + "=" * 50)
print(f"处理完成!")
print(f"成功: {success_count}")
print(f"失败: {failed_count}")
print(f"跳过: {skipped_count}")
print(f"总计: {len(jpg_files)}")
def main():
"""
主函数
"""
parser = argparse.ArgumentParser(description='JPG照片压缩工具')
parser.add_argument('path', help='文件或目录路径')
parser.add_argument('-s', '--size', type=float, default=3, help='目标文件大小(MB),默认3MB')
parser.add_argument('-r', '--recursive', action='store_true', help='递归处理子目录')
parser.add_argument('-q', '--quality', type=int, default=60, help='初始压缩质量(1-95),默认85')
args = parser.parse_args()
if os.path.isfile(args.path):
compress_single_file(args.path, args.size)
elif os.path.isdir(args.path):
compress_directory(args.path, args.size, args.recursive)
else:
print(f"路径不存在: {args.path}")
if __name__ == "__main__":
# 如果没有命令行参数,使用交互模式
if len(os.sys.argv) == 1:
print("JPG照片压缩工具")
print("=" * 30)
path = input("请输入文件或目录路径: ").strip()
if not path:
print("未输入路径,退出程序")
exit()
if os.path.isfile(path):
compress_single_file(path)
elif os.path.isdir(path):
recursive = input("是否递归处理子目录?(y/n): ").lower().strip() == 'y'
compress_directory(path, recursive=recursive)
else:
print(f"路径不存在: {path}")
else:
main()