如何合并两个xcassets资源目录并批量去重?
合并两个XCAssets目录并去除重复文件的方案
针对两个包含大量重复文件的XCAssets目录,可通过以下自动化方案实现合并去重,无需手动操作:
一、Shell命令行脚本(快速处理,基于文件内容哈希)
该方案通过对比文件的SHA256哈希值判断是否重复,适用于仅关注文件内容、无需复杂处理Contents.json的场景。
步骤:
- 创建空的目标合并目录:
mkdir Combined.xcassets
- 先将第一个主题的XCAssets完整复制到目标目录:
cp -r Theme1.xcassets/* Combined.xcassets/
- 执行以下Shell脚本,遍历第二个主题目录,仅复制与目标目录内容不同的文件:
#!/bin/bash SOURCE_DIR="Theme2.xcassets" TARGET_DIR="Combined.xcassets" find "$SOURCE_DIR" -type f -print0 | while IFS= read -r -d $'\0' file; do relative_path="${file#$SOURCE_DIR/}" target_file="$TARGET_DIR/$relative_path" if [ ! -f "$target_file" ]; then mkdir -p "$(dirname "$target_file")" cp "$file" "$target_file" echo "新增文件: $relative_path" continue fi source_hash=$(shasum -a 256 "$file" | awk '{print $1}') target_hash=$(shasum -a 256 "$target_file" | awk '{print $1}') if [ "$source_hash" != "$target_hash" ]; then # 重命名避免覆盖,可根据需求调整命名规则 new_target_file="${target_file%.png}_theme2.png" cp "$file" "$new_target_file" echo "复制差异文件: $relative_path -> ${new_target_file#$TARGET_DIR/}" else echo "跳过重复文件: $relative_path" fi done
二、Python脚本(适配XCAssets结构,处理配置文件)
XCAssets的每个资源目录(如.imageset、.appiconset)都依赖Contents.json配置文件,该脚本会自动合并配置并处理文件去重,更贴合XCAssets的结构特性。
脚本示例:
import os import hashlib import shutil import json def get_file_hash(file_path): sha256_hash = hashlib.sha256() with open(file_path, "rb") as f: for byte_block in iter(lambda: f.read(4096), b""): sha256_hash.update(byte_block) return sha256_hash.hexdigest() def merge_xcassets(source_dir, target_dir): os.makedirs(target_dir, exist_ok=True) for item_name in os.listdir(source_dir): source_item_path = os.path.join(source_dir, item_name) if not os.path.isdir(source_item_path): continue target_item_path = os.path.join(target_dir, item_name) if not os.path.exists(target_item_path): shutil.copytree(source_item_path, target_item_path) print(f"复制完整资源: {item_name}") continue source_files = [f for f in os.listdir(source_item_path) if os.path.isfile(os.path.join(source_item_path, f))] source_json = {} # 先读取源目录的Contents.json if "Contents.json" in source_files: with open(os.path.join(source_item_path, "Contents.json"), "r") as f: source_json = json.load(f) for file_name in source_files: source_file_path = os.path.join(source_item_path, file_name) target_file_path = os.path.join(target_item_path, file_name) if file_name == "Contents.json": with open(target_file_path, "r") as f: target_json = json.load(f) # 合并images配置(按需调整合并逻辑) if "images" in source_json and "images" in target_json: target_filenames = {img.get("filename") for img in target_json["images"]} for img in source_json["images"]: if img.get("filename") not in target_filenames: target_json["images"].append(img) with open(target_file_path, "w") as f: json.dump(target_json, f, indent=2) print(f"合并配置文件: {item_name}/Contents.json") continue if not os.path.exists(target_file_path): shutil.copy2(source_file_path, target_file_path) print(f"新增文件: {item_name}/{file_name}") continue source_hash = get_file_hash(source_file_path) target_hash = get_file_hash(target_file_path) if source_hash != target_hash: base, ext = os.path.splitext(file_name) new_file_name = f"{base}_theme2{ext}" new_target_path = os.path.join(target_item_path, new_file_name) shutil.copy2(source_file_path, new_target_path) # 更新目标目录的Contents.json,添加新文件配置 with open(os.path.join(target_item_path, "Contents.json"), "r+") as f: target_json = json.load(f) if "images" in source_json: for img in source_json["images"]: if img.get("filename") == file_name: new_img = img.copy() new_img["filename"] = new_file_name target_json["images"].append(new_img) json.dump(target_json, f, indent=2) print(f"复制差异文件: {item_name}/{file_name} -> {new_file_name}") else: print(f"跳过重复文件: {item_name}/{file_name}") # 替换为你的实际目录路径 THEME1_DIR = "Theme1.xcassets" THEME2_DIR = "Theme2.xcassets" COMBINED_DIR = "Combined.xcassets" # 先复制第一个主题到合并目录 shutil.copytree(THEME1_DIR, COMBINED_DIR) # 合并第二个主题的内容 merge_xcassets(THEME2_DIR, COMBINED_DIR)
三、关键注意事项
- 备份原始目录:操作前务必备份两个原始XCAssets目录,防止操作失误导致数据丢失。
- 验证合并结果:将合并后的目录导入Xcode,检查所有资源是否能正常加载,配置文件是否正确。
- 处理差异文件:若同名文件内容不同,脚本会自动重命名,后续需根据业务需求决定保留逻辑或调整主题代码。
内容的提问来源于stack exchange,提问作者Steven-Carrot
相关产品推荐
相关产品推荐

