Python文件管理之批量压缩指定目录中的图片

 更新时间:2025年12月04日 09:07:08   作者:零日失眠者  
在数字时代,图片文件占据了我们存储空间的很大一部分,它们往往体积庞大,不仅占用大量存储空间,还会在网络传输时造成延迟,下面我们就来看看如何使用Python自动压缩指定目录中的图片文件吧

简介

在数字时代,图片文件占据了我们存储空间的很大一部分。高清照片、截图和其他图像文件往往体积庞大,不仅占用大量存储空间,还会在网络传输时造成延迟。本文将介绍一个实用的Python脚本——批量图片压缩工具,它可以自动压缩指定目录中的图片文件,在保持可接受画质的前提下显著减小文件大小。

功能介绍

这个批量图片压缩工具具有以下核心功能:

  • 批量压缩:可以同时处理目录中的多个图片文件
  • 质量控制:支持自定义压缩质量参数,平衡文件大小和画质
  • 格式支持:支持JPEG、PNG、BMP等多种常见图片格式
  • 尺寸调整:可选地调整图片尺寸以进一步减小文件大小
  • 进度显示:实时显示处理进度,让用户了解处理状态
  • 备份保留:可选择保留原始文件或直接覆盖
  • 日志记录:记录压缩操作的详细信息,包括压缩率等统计数据

应用场景

这个工具适用于以下场景:

  • 网站优化:压缩网站图片以提高加载速度
  • 存储空间管理:减小图片文件大小以节省存储空间
  • 社交媒体分享:压缩图片以便更快上传到社交平台
  • 邮件附件:减小图片文件大小以便通过邮件发送
  • 移动设备传输:压缩图片以便在移动设备间快速传输
  • 相册整理:批量处理相机照片以节省空间

报错处理

脚本包含了完善的错误处理机制:

  • 文件格式检查:自动识别并跳过不支持的文件格式
  • 路径验证:检查输入目录和输出目录的有效性
  • 权限检测:检测文件读写权限,防止因权限不足导致的错误
  • 内存保护:处理大尺寸图片时防止内存溢出
  • 磁盘空间检查:在处理前检查是否有足够的磁盘空间
  • 异常捕获:捕获并处理运行过程中可能出现的各种异常

代码实现

import os
import sys
import argparse
from PIL import Image
import shutil
from datetime import datetime

class BatchImageCompressor:
    def __init__(self, input_dir, output_dir=None, quality=85, resize_ratio=1.0):
        self.input_dir = input_dir
        self.output_dir = output_dir or input_dir
        self.quality = quality
        self.resize_ratio = resize_ratio
        self.supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp')
        self.compression_log = []
        
    def check_directories(self):
        """检查输入和输出目录"""
        if not os.path.exists(self.input_dir):
            raise FileNotFoundError(f"输入目录 '{self.input_dir}' 不存在")
        
        if not os.path.exists(self.output_dir):
            try:
                os.makedirs(self.output_dir)
                print(f"创建输出目录: {self.output_dir}")
            except Exception as e:
                raise OSError(f"无法创建输出目录 '{self.output_dir}': {e}")
                
    def is_supported_image(self, filename):
        """检查文件是否为支持的图片格式"""
        return filename.lower().endswith(self.supported_formats)
        
    def get_image_files(self):
        """获取目录中的所有图片文件"""
        try:
            files = os.listdir(self.input_dir)
            image_files = [f for f in files if self.is_supported_image(f)]
            return sorted(image_files)
        except Exception as e:
            print(f"读取目录时出错: {e}")
            return []
            
    def get_file_size(self, filepath):
        """获取文件大小(MB)"""
        size_bytes = os.path.getsize(filepath)
        return size_bytes / (1024 * 1024)
        
    def compress_image(self, filename):
        """压缩单个图片文件"""
        input_path = os.path.join(self.input_dir, filename)
        output_path = os.path.join(self.output_dir, filename)
        
        try:
            # 获取原始文件大小
            original_size = self.get_file_size(input_path)
            
            # 打开图片
            with Image.open(input_path) as img:
                # 如果需要调整尺寸
                if self.resize_ratio < 1.0:
                    width, height = img.size
                    new_width = int(width * self.resize_ratio)
                    new_height = int(height * self.resize_ratio)
                    img = img.resize((new_width, new_height), Image.LANCZOS)
                
                # 处理RGBA模式的图片(如PNG)
                if img.mode in ('RGBA', 'LA', 'P'):
                    # 转换为RGB模式以支持JPEG格式
                    if filename.lower().endswith(('.jpg', '.jpeg')):
                        background = Image.new('RGB', img.size, (255, 255, 255))
                        background.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
                        img = background
                
                # 保存压缩后的图片
                if filename.lower().endswith(('.jpg', '.jpeg')):
                    img.save(output_path, 'JPEG', quality=self.quality, optimize=True)
                elif filename.lower().endswith('.png'):
                    img.save(output_path, 'PNG', optimize=True)
                else:
                    img.save(output_path, optimize=True)
                    
            # 获取压缩后文件大小
            compressed_size = self.get_file_size(output_path)
            compression_ratio = (1 - compressed_size / original_size) * 100
            
            return {
                'filename': filename,
                'original_size': original_size,
                'compressed_size': compressed_size,
                'compression_ratio': compression_ratio,
                'status': 'success'
            }
            
        except Exception as e:
            return {
                'filename': filename,
                'error': str(e),
                'status': 'failed'
            }
            
    def process_images(self, backup_original=False):
        """批量处理图片"""
        try:
            self.check_directories()
            image_files = self.get_image_files()
            
            if not image_files:
                print("未找到支持的图片文件")
                return
                
            print(f"找到 {len(image_files)} 个图片文件")
            print(f"压缩质量: {self.quality}%")
            if self.resize_ratio < 1.0:
                print(f"尺寸调整比例: {self.resize_ratio}")
                
            # 如果需要备份且输入输出目录相同,则创建备份目录
            if backup_original and self.input_dir == self.output_dir:
                backup_dir = self.input_dir + "_backup"
                if not os.path.exists(backup_dir):
                    os.makedirs(backup_dir)
                print(f"原始文件将备份到: {backup_dir}")
                
            processed_count = 0
            failed_count = 0
            
            for i, filename in enumerate(image_files, 1):
                print(f"\r处理进度: {i}/{len(image_files)} ({i/len(image_files)*100:.1f}%)", end='')
                
                # 如果需要备份,先复制原始文件
                if backup_original and self.input_dir == self.output_dir:
                    backup_path = os.path.join(self.input_dir + "_backup", filename)
                    if not os.path.exists(backup_path):
                        shutil.copy2(os.path.join(self.input_dir, filename), backup_path)
                
                result = self.compress_image(filename)
                
                if result['status'] == 'success':
                    self.compression_log.append(result)
                    processed_count += 1
                    print(f"\n{filename}: {result['original_size']:.2f}MB -> {result['compressed_size']:.2f}MB "
                          f"(压缩率: {result['compression_ratio']:.1f}%)")
                else:
                    failed_count += 1
                    print(f"\n{filename}: 处理失败 - {result['error']}")
                    
            print(f"\n\n处理完成!")
            print(f"成功处理: {processed_count}")
            print(f"处理失败: {failed_count}")
            
            if processed_count > 0:
                total_original = sum(item['original_size'] for item in self.compression_log)
                total_compressed = sum(item['compressed_size'] for item in self.compression_log)
                avg_compression = (1 - total_compressed / total_original) * 100
                print(f"总压缩率: {avg_compression:.1f}%")
                print(f"节省空间: {total_original - total_compressed:.2f}MB")
                
        except KeyboardInterrupt:
            print("\n\n用户中断操作")
        except Exception as e:
            print(f"\n处理过程中发生错误: {e}")
            
    def save_log(self, log_file="compression_log.txt"):
        """保存压缩日志"""
        try:
            log_path = os.path.join(self.output_dir, log_file)
            with open(log_path, "w", encoding="utf-8") as f:
                f.write("批量图片压缩操作日志\n")
                f.write("=" * 50 + "\n")
                f.write(f"操作时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"输入目录: {self.input_dir}\n")
                f.write(f"输出目录: {self.output_dir}\n")
                f.write(f"压缩质量: {self.quality}%\n")
                f.write(f"尺寸调整比例: {self.resize_ratio}\n\n")
                
                if self.compression_log:
                    f.write("压缩详情:\n")
                    f.write("-" * 50 + "\n")
                    for item in self.compression_log:
                        f.write(f"{item['filename']}:\n")
                        f.write(f"  原始大小: {item['original_size']:.2f}MB\n")
                        f.write(f"  压缩后大小: {item['compressed_size']:.2f}MB\n")
                        f.write(f"  压缩率: {item['compression_ratio']:.1f}%\n\n")
                        
            print(f"操作日志已保存到: {log_file}")
        except Exception as e:
            print(f"保存日志时出错: {e}")

def main():
    parser = argparse.ArgumentParser(description="批量图片压缩工具")
    parser.add_argument("input_dir", help="输入目录路径")
    parser.add_argument("-o", "--output_dir", help="输出目录路径(默认与输入目录相同)")
    parser.add_argument("-q", "--quality", type=int, default=85, 
                       help="压缩质量 (1-100, 默认: 85)")
    parser.add_argument("-r", "--resize", type=float, default=1.0, 
                       help="尺寸调整比例 (0.1-1.0, 默认: 1.0)")
    parser.add_argument("-b", "--backup", action="store_true", 
                       help="备份原始文件")
    
    args = parser.parse_args()
    
    # 验证参数
    if not 1 <= args.quality <= 100:
        print("错误: 压缩质量必须在1-100之间")
        sys.exit(1)
        
    if not 0.1 <= args.resize <= 1.0:
        print("错误: 尺寸调整比例必须在0.1-1.0之间")
        sys.exit(1)
    
    try:
        compressor = BatchImageCompressor(
            input_dir=args.input_dir,
            output_dir=args.output_dir,
            quality=args.quality,
            resize_ratio=args.resize
        )
        
        compressor.process_images(backup_original=args.backup)
        compressor.save_log()
        
    except Exception as e:
        print(f"程序执行出错: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()

使用方法

安装依赖

在使用此脚本之前,需要安装Pillow库:

pip install Pillow

基本使用

# 基本用法,使用默认压缩质量
python image_compressor.py /path/to/images

# 指定压缩质量
python image_compressor.py /path/to/images -q 70

# 指定输出目录
python image_compressor.py /path/to/images -o /path/to/compressed

# 调整图片尺寸(缩小到原来的80%)
python image_compressor.py /path/to/images -r 0.8

# 备份原始文件
python image_compressor.py /path/to/images -b

命令行参数说明

  • input_dir: 必需参数,指定包含图片文件的输入目录
  • -o, --output_dir: 输出目录路径,默认与输入目录相同
  • -q, --quality: 压缩质量,范围1-100,默认85
  • -r, --resize: 尺寸调整比例,范围0.1-1.0,默认1.0(不调整)
  • -b, --backup: 是否备份原始文件

使用示例

假设有以下图片文件(总大小10MB):

photo1.jpg (3MB)
photo2.png (4MB)
screenshot.bmp (3MB)

执行命令:

python image_compressor.py ./images -q 70 -r 0.9

压缩后可能的结果:

photo1.jpg (1.2MB, 压缩率60%)
photo2.png (1.8MB, 压缩率55%)
screenshot.bmp (1.0MB, 压缩率67%)

总节省空间约4MB,压缩率达到60%

总结

这个批量图片压缩工具通过简单的命令行界面提供了高效的图片压缩功能。它支持多种图片格式,允许用户自定义压缩质量和尺寸调整比例,在保持可接受画质的前提下显著减小文件大小。工具还提供了备份功能和详细的日志记录,确保操作的安全性和可追溯性。无论是网站优化、存储空间管理还是日常图片处理,这个工具都能帮助用户轻松完成批量图片压缩任务。

以上就是Python文件管理之批量压缩指定目录中的图片的详细内容,更多关于Python压缩图片的资料请关注脚本之家其它相关文章!

相关文章

  • Python报错:NameError: name ‘xxx‘ is not defined的解决办法

    Python报错:NameError: name ‘xxx‘ is not defined的解决办法

    这篇文章主要给大家介绍了关于Python报错:NameError: name ‘xxx‘ is not defined的解决办法,文中通过代码介绍的非常详细,对大家的学习或者工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2024-06-06
  • python基础之并发编程(三)

    python基础之并发编程(三)

    这篇文章主要介绍了详解python的并发编程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-10-10
  • Python详解如何动态给对象增加属性和方法

    Python详解如何动态给对象增加属性和方法

    python是动态语⾔,动态编程语⾔是⾼级程序设计语⾔的⼀个类别,在计算机科学领域已被⼴泛应⽤。它是⼀类在 运⾏时可以改变其结构 的语⾔ :例如新的函数、对象、甚⾄代码可以被引进,已有的函数可以被删除或是其他结构上的变化
    2022-07-07
  • tensorflow2.0与tensorflow1.0的性能区别介绍

    tensorflow2.0与tensorflow1.0的性能区别介绍

    今天小编就为大家分享一篇tensorflow2.0与tensorflow1.0的性能区别介绍,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-02-02
  • python制图之小提琴图示例代码

    python制图之小提琴图示例代码

    这篇文章主要介绍了python制图之小提琴图的相关资料,提琴图结合箱线图和核密度估计,展示数据分布和概率密度,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-03-03
  • Python 的第三方调试库 ​​​pysnooper​​ 使用示例

    Python 的第三方调试库 ​​​pysnooper​​ 使用示例

    这篇文章主要介绍了Python 的第三方调试库 ​​​pysnooper​​ 使用示例的相关资料,需要的朋友可以参考下
    2023-02-02
  • python用BeautifulSoup库简单爬虫实例分析

    python用BeautifulSoup库简单爬虫实例分析

    文章给大家分享了关于python爬虫的相关实例以及相关代码,有兴趣的朋友们参考下。
    2018-07-07
  • Python中return self的用法详解

    Python中return self的用法详解

    这篇文章主要介绍了Python中return self的用法,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-07-07
  • python编程使用selenium编写测试用例

    python编程使用selenium编写测试用例

    这篇文章主要为大家介绍了在python编程学习中如何使用selenium来编写测试用例,文中给出了详细的测试用例代码,有需要的朋友可以借鉴参考下
    2021-10-10
  • Python自动化办公之PPT段落的使用

    Python自动化办公之PPT段落的使用

    这篇文章将详细为大家介绍一些Python中PPT段落的一些使用:获取段落、段落添加内容、自定义段落等,文中的示例代码讲解详细,需要的可以参考一下
    2022-05-05

最新评论