本文目录导读:

使用 Python + Pillow(推荐)
#!/usr/bin/env python3
"""
批量去除图片元数据脚本
支持格式: JPG, PNG, WebP, TIFF, BMP
"""
import os
import sys
from PIL import Image
from concurrent.futures import ThreadPoolExecutor, as_completed
def remove_metadata(input_path, output_path=None, inplace=False):
"""
去除单张图片的元数据
Args:
input_path: 输入文件路径
output_path: 输出文件路径(inplace=False时使用)
inplace: 是否原地替换原文件
"""
try:
img = Image.open(input_path)
# 获取图片格式
img_format = img.format
if img_format is None:
img_format = input_path.split('.')[-1].upper()
if img_format in ['JPG', 'JPEG']:
img_format = 'JPEG'
# 提取可用的数据(不包含元数据)
# 对于PNG,需要保留alpha通道
if img_format == 'PNG' and img.mode in ('RGBA', 'LA', 'PA'):
data = img.getdata()
img_clean = Image.new(img.mode, img.size)
img_clean.putdata(list(data))
else:
# 对于其他格式,直接复制并丢弃元数据
if img.mode == 'RGBA':
img_clean = Image.new('RGBA', img.size)
elif img.mode == 'P':
img_clean = Image.new('RGBA', img.size)
else:
img_clean = Image.new('RGB', img.size)
img_clean.putdata(list(img.getdata()))
# 保存(不保留任何元数据)
if inplace:
# 先保存到临时文件,再替换
temp_path = input_path + '.temp_clean'
img_clean.save(temp_path, format=img_format, quality=95)
os.replace(temp_path, input_path)
else:
img_clean.save(output_path, format=img_format, quality=95)
return True, input_path, None
except Exception as e:
return False, input_path, str(e)
def batch_remove_metadata(input_dir, output_dir=None, inplace=False):
"""
批量去除图片元数据
Args:
input_dir: 输入目录
output_dir: 输出目录(inplace=False时使用)
inplace: 是否原地替换
"""
supported_ext = {'.jpg', '.jpeg', '.png', '.webp', '.tiff', '.tif', '.bmp'}
# 获取所有图片文件
files = []
for root, dirs, filenames in os.walk(input_dir):
for filename in filenames:
ext = os.path.splitext(filename)[1].lower()
if ext in supported_ext:
files.append(os.path.join(root, filename))
if not files:
print("没有找到支持的图片文件")
return
print(f"找到 {len(files)} 个图片文件")
# 创建输出目录(如果需要)
if output_dir and not inplace:
os.makedirs(output_dir, exist_ok=True)
# 使用线程池处理
success_count = 0
fail_count = 0
with ThreadPoolExecutor(max_workers=8) as executor:
futures = []
for file_path in files:
if inplace:
futures.append(executor.submit(remove_metadata, file_path, inplace=True))
else:
# 保留目录结构
rel_path = os.path.relpath(file_path, input_dir)
out_path = os.path.join(output_dir, rel_path)
os.makedirs(os.path.dirname(out_path), exist_ok=True)
futures.append(executor.submit(remove_metadata, file_path, out_path))
for future in as_completed(futures):
success, path, error = future.result()
if success:
success_count += 1
print(f"✓ 处理成功: {path}")
else:
fail_count += 1
print(f"✗ 处理失败: {path} - {error}")
print(f"\n处理完成: 成功 {success_count}, 失败 {fail_count}")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="批量去除图片元数据")
parser.add_argument("input", help="输入目录")
parser.add_argument("-o", "--output", help="输出目录(不指定则原地替换)")
parser.add_argument("--inplace", action="store_true", help="原地替换原文件")
args = parser.parse_args()
if args.inplace or args.output is None:
print("警告: 将原地修改文件!")
confirm = input("确认继续? (y/n): ")
if confirm.lower() != 'y':
sys.exit(0)
batch_remove_metadata(args.input, inplace=True)
else:
batch_remove_metadata(args.input, args.output)
使用 exiftool(更彻底)
#!/usr/bin/env python3
"""
使用exiftool批量去除图片元数据
需要先安装: exiftool
"""
import os
import subprocess
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
def check_exiftool():
"""检查exiftool是否安装"""
try:
subprocess.run(['exiftool', '-ver'], capture_output=True, check=True)
return True
except (subprocess.CalledProcessError, FileNotFoundError):
return False
def remove_metadata_exiftool(file_path, inplace=True):
"""
使用exiftool去除元数据
Args:
file_path: 文件路径
inplace: 是否原地修改
"""
try:
if inplace:
# 原地修改,保留备份
subprocess.run(
['exiftool', '-overwrite_original', '-all=', file_path],
capture_output=True,
check=True
)
else:
# 保存到新文件
base, ext = os.path.splitext(file_path)
output_path = f"{base}_clean{ext}"
subprocess.run(
['exiftool', '-all=', '-o', output_path, file_path],
capture_output=True,
check=True
)
return True, file_path, None
except subprocess.CalledProcessError as e:
return False, file_path, str(e)
def batch_remove_metadata_exiftool(input_dir, inplace=True):
"""
批量使用exiftool去除元数据
"""
if not check_exiftool():
print("错误: 未安装exiftool,请通过以下方式安装:")
print(" macOS: brew install exiftool")
print(" Linux: sudo apt-get install exiftool")
print(" Windows: 下载 https://exiftool.org/")
sys.exit(1)
supported_ext = {'.jpg', '.jpeg', '.png', '.webp', '.tiff', '.tif'}
# 获取所有图片文件
files = []
for root, dirs, filenames in os.walk(input_dir):
for filename in filenames:
ext = os.path.splitext(filename)[1].lower()
if ext in supported_ext:
files.append(os.path.join(root, filename))
if not files:
print("没有找到支持的图片文件")
return
print(f"找到 {len(files)} 个图片文件")
# 使用多线程处理
success_count = 0
fail_count = 0
with ThreadPoolExecutor(max_workers=4) as executor:
futures = []
for file_path in files:
futures.append(executor.submit(remove_metadata_exiftool, file_path, inplace))
for future in as_completed(futures):
success, path, error = future.result()
if success:
success_count += 1
print(f"✓ 处理成功: {path}")
else:
fail_count += 1
print(f"✗ 处理失败: {path} - {error}")
print(f"\n处理完成: 成功 {success_count}, 失败 {fail_count}")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("用法: python script.py <输入目录> [--no-backup]")
sys.exit(1)
input_dir = sys.argv[1]
inplace = True # 默认原地修改
if len(sys.argv) > 2 and sys.argv[2] == '--no-backup':
confirm = input("警告: 将永久修改文件,是否继续? (y/n): ")
if confirm.lower() != 'y':
sys.exit(0)
batch_remove_metadata_exiftool(input_dir, inplace)
shell脚本(Linux/macOS)
#!/bin/bash
# 批量去除图片元数据的Shell脚本
# 使用方法: ./remove_exif.sh [输入目录] [输出目录]
# 如果不指定输出目录,则原地修改
INPUT_DIR="${1:-.}"
OUTPUT_DIR="${2:-}"
# 支持的格式
EXTENSIONS="jpg jpeg png webp tiff tif"
# 检查exiftool
if ! command -v exiftool &> /dev/null; then
echo "错误: 需要安装exiftool"
echo "安装方式: brew install exiftool (macOS)"
echo " sudo apt-get install exiftool (Linux)"
exit 1
fi
# 创建输出目录
if [ -n "$OUTPUT_DIR" ]; then
mkdir -p "$OUTPUT_DIR"
fi
# 处理函数
process_file() {
local file="$1"
local rel_path="${file#$INPUT_DIR/}"
if [ -n "$OUTPUT_DIR" ]; then
# 输出到新目录
output_path="$OUTPUT_DIR/$rel_path"
mkdir -p "$(dirname "$output_path")"
exiftool -all= -o "$output_path" "$file" >/dev/null 2>&1
else
# 原地修改
exiftool -overwrite_original -all= "$file" >/dev/null 2>&1
fi
if [ $? -eq 0 ]; then
echo "✓ $file"
else
echo "✗ $file"
fi
}
export -f process_file
export INPUT_DIR OUTPUT_DIR
# 遍历处理
echo "开始处理图片文件..."
for ext in $EXTENSIONS; do
find "$INPUT_DIR" -type f -iname "*.$ext" -exec bash -c 'process_file "$0"' {} \;
done
echo "处理完成!"
使用说明
安装依赖
Python方案:
pip install Pillow
exiftool方案:
# macOS brew install exiftool # Linux (Ubuntu/Debian) sudo apt-get install exiftool # Windows # 下载 https://exiftool.org/ 并安装
运行脚本
Python脚本:
# 原地修改 python remove_metadata.py ./images --inplace # 输出到新目录 python remove_metadata.py ./images -o ./cleaned_images
Shell脚本:
# 原地修改 chmod +x remove_exif.sh ./remove_exif.sh ./images # 输出到新目录 ./remove_exif.sh ./images ./cleaned_images
注意事项
- 备份重要文件:建议先对重要图片进行备份
- PSD文件:不支持PSD格式
- GIF动画:可能无法保持动画效果
- EXIF数据:exiftool方式可以完全去除,Pillow方式可能保留部分数据
选择哪种方案取决于你的具体需求和对第三方工具的偏好。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播