- 新增websocket客户端和服务端实现 - 添加图片压缩工具和快速压缩脚本 - 实现学生信息处理相关API - 添加MQTT客户端和消息处理功能 - 更新.gitignore忽略更多文件类型 - 添加数据库操作工具和示例 - 实现多个测试脚本和工具类
234 lines
8.6 KiB
Python
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() |