You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何合并两个xcassets资源目录并批量去重?

合并两个XCAssets目录并去除重复文件的方案

针对两个包含大量重复文件的XCAssets目录,可通过以下自动化方案实现合并去重,无需手动操作:

一、Shell命令行脚本(快速处理,基于文件内容哈希)

该方案通过对比文件的SHA256哈希值判断是否重复,适用于仅关注文件内容、无需复杂处理Contents.json的场景。

步骤:

  1. 创建空的目标合并目录:
mkdir Combined.xcassets
  1. 先将第一个主题的XCAssets完整复制到目标目录:
cp -r Theme1.xcassets/* Combined.xcassets/
  1. 执行以下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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 15:25:24