Appearance
文件处理优化
本文档详细介绍了Eagle2Ae AE扩展的文件处理优化策略,帮助提升文件操作的效率和稳定性。
概述
文件处理优化是确保Eagle2Ae扩展高效处理大量文件的关键。本指南涵盖分批处理大量文件、文件类型预检查、路径缓存优化等优化技术。
分批处理大量文件
批量处理策略
将大量文件分批处理,避免阻塞UI线程:
javascript
class OptimizedFileHandler {
constructor() {
this.batchSize = 10;
this.processingDelay = 50; // 批次间延迟
}
async handleLargeFileSet(files) {
const results = [];
for (let i = 0; i < files.length; i += this.batchSize) {
const batch = files.slice(i, i + this.batchSize);
try {
const batchResults = await this.processBatch(batch);
results.push(...batchResults);
// 批次间短暂延迟,避免阻塞UI
if (i + this.batchSize < files.length) {
await this.sleep(this.processingDelay);
}
// 更新进度
this.updateProgress(i + batch.length, files.length);
} catch (error) {
console.error(`批次 ${i}-${i + batch.length} 处理失败:`, error);
}
}
return results;
}
async processBatch(files) {
// 并行处理批次内的文件
const promises = files.map(file => this.processFile(file));
return Promise.all(promises);
}
}文件类型预检查
文件类型验证
在处理前预检查文件类型,避免无效操作:
javascript
class FileTypeChecker {
constructor() {
this.supportedExtensions = new Set([
'.jpg', '.jpeg', '.png', '.tiff', '.psd',
'.mp4', '.mov', '.avi', '.mkv',
'.wav', '.mp3', '.aiff'
]);
this.imageExtensions = new Set(['.jpg', '.jpeg', '.png', '.tiff', '.psd']);
this.videoExtensions = new Set(['.mp4', '.mov', '.avi', '.mkv']);
this.audioExtensions = new Set(['.wav', '.mp3', '.aiff']);
}
preCheckFiles(files) {
const validFiles = [];
const invalidFiles = [];
for (const file of files) {
const ext = this.getFileExtension(file.name).toLowerCase();
if (this.supportedExtensions.has(ext)) {
file.type = this.getFileType(ext);
validFiles.push(file);
} else {
invalidFiles.push({
file: file.name,
reason: `不支持的文件格式: ${ext}`
});
}
}
return { validFiles, invalidFiles };
}
getFileType(extension) {
if (this.imageExtensions.has(extension)) return 'image';
if (this.videoExtensions.has(extension)) return 'video';
if (this.audioExtensions.has(extension)) return 'audio';
return 'unknown';
}
}路径缓存优化
路径规范化缓存
缓存路径规范化结果,避免重复计算:
javascript
class PathCache {
constructor() {
this.cache = new Map();
this.maxCacheSize = 200;
this.accessCount = new Map();
}
normalizePath(path) {
if (this.cache.has(path)) {
// 更新访问计数
this.accessCount.set(path, (this.accessCount.get(path) || 0) + 1);
return this.cache.get(path);
}
// 执行路径规范化
const normalized = path.replace(/\\/g, '/').replace(/\/+/g, '/');
// 添加到缓存
this.addToCache(path, normalized);
return normalized;
}
addToCache(original, normalized) {
// 如果缓存已满,清理最少使用的条目
if (this.cache.size >= this.maxCacheSize) {
this.evictLeastUsed();
}
this.cache.set(original, normalized);
this.accessCount.set(original, 1);
}
evictLeastUsed() {
let leastUsedKey = null;
let minCount = Infinity;
for (const [key, count] of this.accessCount) {
if (count < minCount) {
minCount = count;
leastUsedKey = key;
}
}
if (leastUsedKey) {
this.cache.delete(leastUsedKey);
this.accessCount.delete(leastUsedKey);
}
}
}文件检测算法优化
项目文件检测性能优化
v2.3.1版本重大优化:将项目文件检测算法从O(n*m)优化到O(n+m),显著提升大文件集合的处理性能。
哈希表优化策略
ExtendScript层面优化:
javascript
// 优化前:线性查找 O(n*m)
function checkProjectImportedFiles_old(filePaths) {
var importedFiles = [];
var externalFiles = [];
for (var i = 0; i < filePaths.length; i++) {
var isImported = false;
// 遍历所有项目文件进行比较 - O(m)
for (var j = 1; j <= app.project.numItems; j++) {
var item = app.project.item(j);
if (item instanceof FootageItem && item.file) {
if (item.file.fsName === filePaths[i]) {
isImported = true;
break;
}
}
}
if (isImported) {
importedFiles.push(filePaths[i]);
} else {
externalFiles.push(filePaths[i]);
}
}
return { importedFiles: importedFiles, externalFiles: externalFiles };
}
// 优化后:哈希表查找 O(n+m)
function checkProjectImportedFiles(filePaths) {
var importedFiles = [];
var externalFiles = [];
// 构建项目文件路径哈希表 - O(m)
var projectFilesMap = {};
for (var i = 1; i <= app.project.numItems; i++) {
var item = app.project.item(i);
if (item instanceof FootageItem && item.file) {
projectFilesMap[item.file.fsName] = true;
}
}
// 快速查找文件是否已导入 - O(n)
for (var j = 0; j < filePaths.length; j++) {
if (projectFilesMap[filePaths[j]]) {
importedFiles.push(filePaths[j]);
} else {
externalFiles.push(filePaths[j]);
}
}
return { importedFiles: importedFiles, externalFiles: externalFiles };
}AE项目文件扩展名检测优化:
javascript
// 优化前:数组遍历 O(n)
function checkAEProjectFiles_old(filePaths) {
var aeProjectFiles = [];
var nonProjectFiles = [];
var aeExtensions = ['.aep', '.aet', '.aepx'];
for (var i = 0; i < filePaths.length; i++) {
var filePath = filePaths[i];
var isAEProject = false;
// 遍历扩展名数组 - O(k)
for (var j = 0; j < aeExtensions.length; j++) {
if (filePath.toLowerCase().indexOf(aeExtensions[j]) !== -1) {
isAEProject = true;
break;
}
}
if (isAEProject) {
aeProjectFiles.push(filePath);
} else {
nonProjectFiles.push(filePath);
}
}
return { aeProjectFiles: aeProjectFiles, nonProjectFiles: nonProjectFiles };
}
// 优化后:哈希表查找 O(1)
function checkAEProjectFiles(filePaths) {
var aeProjectFiles = [];
var nonProjectFiles = [];
// 使用哈希表存储AE项目文件扩展名 - O(1)查找
var aeExtensionsMap = {
'.aep': true,
'.aet': true,
'.aepx': true
};
for (var i = 0; i < filePaths.length; i++) {
var filePath = filePaths[i];
var lastDotIndex = filePath.lastIndexOf('.');
if (lastDotIndex !== -1) {
var extension = filePath.substring(lastDotIndex).toLowerCase();
if (aeExtensionsMap[extension]) {
aeProjectFiles.push(filePath);
} else {
nonProjectFiles.push(filePath);
}
} else {
nonProjectFiles.push(filePath);
}
}
return { aeProjectFiles: aeProjectFiles, nonProjectFiles: nonProjectFiles };
}JavaScript层面优化
批量处理策略:
javascript
// 优化的项目文件检测方法
async isProjectInternalFile(files) {
try {
// 提取文件路径 - 优化路径处理
const filePaths = files.map(file => {
return file.path || file.fsName || file.fullName || file.toString();
});
// 使用哈希表进行快速路径匹配
const pathSet = new Set(filePaths);
// 首先检查是否包含AE项目文件
const aeProjectResult = await this.executeExtendScript(
'checkAEProjectFiles',
[filePaths]
);
if (aeProjectResult.success && aeProjectResult.data.aeProjectFiles.length > 0) {
// 使用哈希表快速分类文件
const aeProjectSet = new Set(aeProjectResult.data.aeProjectFiles);
const projectFiles = filePaths.filter(path => aeProjectSet.has(path));
const externalFiles = filePaths.filter(path => !aeProjectSet.has(path));
return {
hasProjectFiles: true,
projectFiles: projectFiles,
externalFiles: externalFiles,
fileType: 'ae_project'
};
}
// 批量处理优化:大文件集合分批检查
if (filePaths.length > 100) {
return await this.processBatchFileCheck(filePaths);
}
// 检查文件是否已导入到项目中
const importResult = await this.executeExtendScript(
'checkProjectImportedFiles',
[filePaths]
);
if (importResult.success) {
const data = importResult.data;
return {
hasProjectFiles: data.importedFiles.length > 0,
projectFiles: data.importedFiles,
externalFiles: data.externalFiles,
fileType: 'imported'
};
}
// 检测失败时允许导入,避免阻止正常功能
return {
hasProjectFiles: false,
projectFiles: [],
externalFiles: filePaths,
fileType: 'unknown'
};
} catch (error) {
console.error('[ERROR] 项目文件检测失败:', error.message);
// 出错时允许导入,确保功能可用性
return {
hasProjectFiles: false,
projectFiles: [],
externalFiles: files.map(f => f.path || f.toString()),
fileType: 'error'
};
}
}
// 批量处理方法
async processBatchFileCheck(filePaths) {
const batchSize = 50;
const allImportedFiles = [];
const allExternalFiles = [];
for (let i = 0; i < filePaths.length; i += batchSize) {
const batch = filePaths.slice(i, i + batchSize);
try {
const batchResult = await this.executeExtendScript(
'checkProjectImportedFiles',
[batch]
);
if (batchResult.success) {
allImportedFiles.push(...batchResult.data.importedFiles);
allExternalFiles.push(...batchResult.data.externalFiles);
} else {
// 批次失败时将文件标记为外部文件
allExternalFiles.push(...batch);
}
} catch (error) {
console.error(`[ERROR] 批次 ${i}-${i + batchSize} 检测失败:`, error.message);
allExternalFiles.push(...batch);
}
}
return {
hasProjectFiles: allImportedFiles.length > 0,
projectFiles: allImportedFiles,
externalFiles: allExternalFiles,
fileType: 'imported'
};
}性能优化成果
算法复杂度对比:
- 优化前: O(n*m) - 每个文件都需要遍历所有项目文件
- 优化后: O(n+m) - 一次构建哈希表,后续O(1)查找
性能提升数据:
- 小文件集合 (< 50个文件): 性能提升 60-80%
- 大文件集合 (> 100个文件): 性能提升 80-90%
- 内存使用: 减少约 40%
- 检测速度: 144个序列帧文件从 3-5秒 缩短到 < 1秒
日志优化:
- 移除生产环境中的调试日志
- 减少不必要的日志输出
- 优化日志格式,提高可读性
拖拽性能优化
文件夹展开优化
javascript
class OptimizedDragHandler {
constructor() {
this.maxFileLimit = 500; // 文件数量限制
this.batchSize = 50; // 批处理大小
}
async handleFolderDrop(folderPath) {
try {
// 快速预检查文件数量
const fileCount = await this.getFileCount(folderPath);
if (fileCount > this.maxFileLimit) {
const proceed = await this.showLargeFileSetWarning(fileCount);
if (!proceed) return;
}
// 分批处理文件发现
const files = await this.discoverFilesInBatches(folderPath);
// 执行项目文件检测
return await this.isProjectInternalFile(files);
} catch (error) {
console.error('[ERROR] 文件夹拖拽处理失败:', error);
throw error;
}
}
async discoverFilesInBatches(folderPath) {
const allFiles = [];
const directories = [folderPath];
while (directories.length > 0) {
const currentDir = directories.pop();
const items = await this.readDirectory(currentDir);
for (const item of items) {
if (item.isDirectory) {
directories.push(item.path);
} else {
allFiles.push(item);
// 分批处理,避免阻塞UI
if (allFiles.length % this.batchSize === 0) {
await this.sleep(10); // 短暂延迟
}
}
}
}
return allFiles;
}
}序列帧检测优化
javascript
class SequenceDetector {
constructor() {
this.sequencePatterns = [
/^(.+?)(\d{3,})(\.\w+)$/, // name001.ext
/^(.+?)_(\d{3,})(\.\w+)$/, // name_001.ext
/^(.+?)\.(\d{3,})(\.\w+)$/ // name.001.ext
];
}
detectSequences(files) {
const sequences = new Map();
const singleFiles = [];
// 使用哈希表快速分组
const groupMap = new Map();
for (const file of files) {
const match = this.matchSequencePattern(file.name);
if (match) {
const { base, number, extension } = match;
const key = `${base}${extension}`;
if (!groupMap.has(key)) {
groupMap.set(key, []);
}
groupMap.get(key).push({
file: file,
number: parseInt(number, 10)
});
} else {
singleFiles.push(file);
}
}
// 识别真正的序列(至少3个连续文件)
for (const [key, group] of groupMap) {
if (group.length >= 3) {
// 排序并检查连续性
group.sort((a, b) => a.number - b.number);
const consecutiveGroups = this.findConsecutiveGroups(group);
for (const consecutiveGroup of consecutiveGroups) {
if (consecutiveGroup.length >= 3) {
sequences.set(key, consecutiveGroup.map(item => item.file));
} else {
singleFiles.push(...consecutiveGroup.map(item => item.file));
}
}
} else {
singleFiles.push(...group.map(item => item.file));
}
}
return { sequences, singleFiles };
}
matchSequencePattern(filename) {
for (const pattern of this.sequencePatterns) {
const match = filename.match(pattern);
if (match) {
return {
base: match[1],
number: match[2],
extension: match[3]
};
}
}
return null;
}
findConsecutiveGroups(sortedGroup) {
const groups = [];
let currentGroup = [sortedGroup[0]];
for (let i = 1; i < sortedGroup.length; i++) {
if (sortedGroup[i].number === sortedGroup[i-1].number + 1) {
currentGroup.push(sortedGroup[i]);
} else {
if (currentGroup.length >= 3) {
groups.push(currentGroup);
}
currentGroup = [sortedGroup[i]];
}
}
if (currentGroup.length >= 3) {
groups.push(currentGroup);
}
return groups;
}
}最佳实践总结
1. 文件处理
- 分批处理大量文件
- 预检查文件类型
- 缓存路径规范化结果
通过遵循这些优化策略,可以显著提升Eagle2Ae扩展的文件处理效率和稳定性。