feat: 添加多个功能模块和工具脚本
- 新增websocket客户端和服务端实现 - 添加图片压缩工具和快速压缩脚本 - 实现学生信息处理相关API - 添加MQTT客户端和消息处理功能 - 更新.gitignore忽略更多文件类型 - 添加数据库操作工具和示例 - 实现多个测试脚本和工具类
This commit is contained in:
+234
@@ -0,0 +1,234 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user