解决cocos引擎编译文件.cconb.bin文件被过滤问题
蔬菜土豆泥
2026年08月31日 20:39

Toy文件容器存在文件后缀白名单,开发者使用cocos发布内容时部分文件会被容器安全控件过滤。导致预览和正式页面因文件缺失报错。

后缀白名单参见:常见问题​

该问题可以通过js脚本实现全局替换来解决。

这里是我自己写的脚本,请将该js文件放在build文件夹下面。在cocos编辑器完成发布后,通过运行 node toy.js实现全局文件后缀替换。

脚本内容如下:

代码块
JavaScript
自动换行
复制代码
const fs = require('fs');
const path = require('path');

// 目标文件夹路径
const targetDir = './web-mobile';

// 定义扩展名映射
const extensionMap = {
    '.cconb': '.unityweb',
    '.bin': '.part'
};

// 需要处理的文件类型
const targetFileTypes = ['.js', '.json'];

// 递归遍历文件夹
function walkDir(dir, callback) {
    const files = fs.readdirSync(dir);
    files.forEach(file => {
        const filePath = path.join(dir, file);
        const stats = fs.statSync(filePath);
        if (stats.isDirectory()) {
            walkDir(filePath, callback);
        } else {
            callback(filePath);
        }
    });
}

// 精确替换双引号中的完整扩展名
function replaceExactExtension(content, oldExt, newExt) {
    // 转义特殊字符
    const escapedOldExt = oldExt.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    
    // 匹配双引号中的内容,且扩展名前后必须是引号或路径分隔符
    // 匹配模式: "xxx.cconb" 或 "xxx/yyy.cconb"
    const pattern = new RegExp(`"([^"]*)${escapedOldExt}([^"]*)"`, 'g');
    
    let modified = false;
    let replacements = 0;
    let result = content;
    
    // 找到所有匹配
    let match;
    while ((match = pattern.exec(content)) !== null) {
        const fullMatch = match[0];
        const prefix = match[1];
        const suffix = match[2];
        
        // 检查扩展名是否完整(后面没有多余的字母或数字)
        // 如果后缀为空或以引号结束,说明扩展名是完整的
        if (suffix === '' || suffix.startsWith('"')) {
            // 检查扩展名是否正好在字符串末尾(引号前)
            const stringEnd = fullMatch.lastIndexOf('"');
            const extStart = fullMatch.indexOf(oldExt);
            
            // 确保扩展名后面紧跟着引号
            if (extStart + oldExt.length === fullMatch.length - 1) {
                // 完全匹配,替换
                const newStr = fullMatch.replace(oldExt, newExt);
                result = result.replace(fullMatch, newStr);
                replacements++;
                modified = true;
            }
        }
    }
    
    return { content: result, modified, replacements };
}

// 替换文件内容中的字符串
function replaceFileContent(filePath) {
    try {
        // 读取文件内容
        let content = fs.readFileSync(filePath, 'utf8');
        let totalModified = false;
        let totalReplacements = 0;
        const fileName = path.basename(filePath);
        const ext = path.extname(filePath);

        for (const [oldExt, newExt] of Object.entries(extensionMap)) {
            let result;
            
            if (ext === '.js' || ext === '.json') {
                // 精确替换
                result = replaceExactExtension(content, oldExt, newExt);
            } else {
                continue;
            }
            
            if (result.modified) {
                content = result.content;
                totalModified = true;
                totalReplacements += result.replacements;
            }
        }

        // 如果有修改,写回文件
        if (totalModified) {
            fs.writeFileSync(filePath, content, 'utf8');
            console.log(`📝 内容替换: ${fileName} (替换了 ${totalReplacements} 处)`);
            return { fileName, replacementCount: totalReplacements };
        }
        return null;
    } catch (err) {
        console.error(`❌ 读取/写入文件失败: ${filePath}`, err.message);
        return null;
    }
}

// 主函数
function processFiles() {
    // 检查目标文件夹是否存在
    if (!fs.existsSync(targetDir)) {
        console.error(`错误: 文件夹 "${targetDir}" 不存在`);
        return;
    }

    console.log('🚀 开始处理文件...\n');

    let renameCount = 0;
    let errorCount = 0;
    let replaceCount = 0;
    const renameLog = [];
    const replaceLog = [];

    // 第一步:重命名文件
    console.log('📁 第一步:重命名文件');
    console.log('─'.repeat(50));

    walkDir(targetDir, (filePath) => {
        // 检查文件扩展名是否需要替换
        let shouldRename = false;
        let newPath = filePath;

        for (const [oldExt, newExt] of Object.entries(extensionMap)) {
            if (filePath.endsWith(oldExt)) {
                newPath = filePath.replace(new RegExp(`${oldExt.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`), newExt);
                shouldRename = true;
                break;
            }
        }

        if (shouldRename) {
            try {
                fs.renameSync(filePath, newPath);
                const oldName = path.basename(filePath);
                const newName = path.basename(newPath);
                console.log(`✅ 重命名: ${oldName} -> ${newName}`);
                renameCount++;
                renameLog.push({ old: filePath, new: newPath });
            } catch (err) {
                console.error(`❌ 重命名失败: ${filePath}`, err.message);
                errorCount++;
            }
        }
    });

    console.log(`\n📊 重命名完成: ${renameCount} 个文件\n`);

    // 第二步:替换文件内容
    console.log('📝 第二步:替换文件内容(精确匹配)');
    console.log('─'.repeat(50));

    walkDir(targetDir, (filePath) => {
        const ext = path.extname(filePath);
        // 只处理 .js 和 .json 文件
        if (targetFileTypes.includes(ext)) {
            const result = replaceFileContent(filePath);
            if (result) {
                replaceCount++;
                replaceLog.push(result);
            }
        }
    });

    // 显示汇总信息
    console.log('\n' + '='.repeat(60));
    console.log('📊 处理完成汇总');
    console.log('='.repeat(60));
    console.log(`📁 重命名文件: ${renameCount} 个`);
    console.log(`📝 内容替换文件: ${replaceCount} 个`);

    if (renameLog.length > 0) {
        console.log('\n📋 重命名详情:');
        const cconbCount = renameLog.filter(item => item.old.endsWith('.cconb')).length;
        const binCount = renameLog.filter(item => item.old.endsWith('.bin')).length;
        console.log(`   - .cconb -> .unityweb: ${cconbCount} 个`);
        console.log(`   - .bin -> .part: ${binCount} 个`);
    }

    if (replaceLog.length > 0) {
        console.log('\n📋 内容替换详情:');
        let totalReplacements = 0;
        replaceLog.forEach(item => {
            console.log(`   - ${item.fileName}: ${item.replacementCount} 处`);
            totalReplacements += item.replacementCount;
        });
        console.log(`   📈 总计替换: ${totalReplacements} 处`);
    }

    console.log('\n✅ 所有处理完成!');
}

// 执行主函数
processFiles();
复制成功