You've already forked Nano-Banana-AI-Image-Editor
修复内存溢出问题
This commit is contained in:
@@ -6,6 +6,10 @@ const DB_VERSION = 1;
|
||||
const GENERATIONS_STORE = 'generations';
|
||||
const EDITS_STORE = 'edits';
|
||||
|
||||
// 重试配置
|
||||
const MAX_RETRIES = 3;
|
||||
const RETRY_DELAY = 1000;
|
||||
|
||||
// IndexedDB实例
|
||||
let db: IDBDatabase | null = null;
|
||||
|
||||
@@ -59,12 +63,50 @@ const getDB = (): IDBDatabase => {
|
||||
* 添加生成记录
|
||||
*/
|
||||
export const addGeneration = async (generation: Generation): Promise<void> => {
|
||||
// 创建轻量级生成记录,只存储必要的信息和上传后的URL
|
||||
const lightweightGeneration = {
|
||||
id: generation.id,
|
||||
prompt: generation.prompt,
|
||||
parameters: generation.parameters,
|
||||
modelVersion: generation.modelVersion,
|
||||
timestamp: generation.timestamp,
|
||||
uploadResults: generation.uploadResults,
|
||||
usageMetadata: generation.usageMetadata,
|
||||
// 只存储上传后的URL,不存储base64数据
|
||||
sourceAssets: generation.sourceAssets.map(asset => {
|
||||
const uploadedUrl = getUploadedAssetUrl(generation, asset.id, 'source');
|
||||
return {
|
||||
id: asset.id,
|
||||
type: asset.type,
|
||||
// 如果没有上传后的URL,则不存储URL以避免base64数据
|
||||
url: uploadedUrl || '',
|
||||
mime: asset.mime,
|
||||
width: asset.width,
|
||||
height: asset.height,
|
||||
checksum: asset.checksum
|
||||
};
|
||||
}),
|
||||
outputAssets: generation.outputAssets.map(asset => {
|
||||
const uploadedUrl = getUploadedAssetUrl(generation, asset.id, 'output');
|
||||
return {
|
||||
id: asset.id,
|
||||
type: asset.type,
|
||||
// 如果没有上传后的URL,则不存储URL以避免base64数据
|
||||
url: uploadedUrl || '',
|
||||
mime: asset.mime,
|
||||
width: asset.width,
|
||||
height: asset.height,
|
||||
checksum: asset.checksum
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
const db = getDB();
|
||||
const transaction = db.transaction([GENERATIONS_STORE], 'readwrite');
|
||||
const store = transaction.objectStore(GENERATIONS_STORE);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.add(generation);
|
||||
const request = store.add(lightweightGeneration);
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
@@ -74,17 +116,95 @@ export const addGeneration = async (generation: Generation): Promise<void> => {
|
||||
* 添加编辑记录
|
||||
*/
|
||||
export const addEdit = async (edit: Edit): Promise<void> => {
|
||||
// 创建轻量级编辑记录,只存储必要的信息和上传后的URL
|
||||
const lightweightEdit = {
|
||||
id: edit.id,
|
||||
parentGenerationId: edit.parentGenerationId,
|
||||
maskAssetId: edit.maskAssetId,
|
||||
instruction: edit.instruction,
|
||||
timestamp: edit.timestamp,
|
||||
uploadResults: edit.uploadResults,
|
||||
parameters: edit.parameters,
|
||||
usageMetadata: edit.usageMetadata,
|
||||
// 只存储上传后的URL,不存储base64数据
|
||||
maskReferenceAsset: edit.maskReferenceAsset ? (() => {
|
||||
const uploadedUrl = getUploadedAssetUrl(edit, edit.maskReferenceAsset.id, 'mask');
|
||||
return {
|
||||
id: edit.maskReferenceAsset.id,
|
||||
type: edit.maskReferenceAsset.type,
|
||||
// 如果没有上传后的URL,则不存储URL以避免base64数据
|
||||
url: uploadedUrl || '',
|
||||
mime: edit.maskReferenceAsset.mime,
|
||||
width: edit.maskReferenceAsset.width,
|
||||
height: edit.maskReferenceAsset.height,
|
||||
checksum: edit.maskReferenceAsset.checksum
|
||||
};
|
||||
})() : undefined,
|
||||
outputAssets: edit.outputAssets.map(asset => {
|
||||
const uploadedUrl = getUploadedAssetUrl(edit, asset.id, 'output');
|
||||
return {
|
||||
id: asset.id,
|
||||
type: asset.type,
|
||||
// 如果没有上传后的URL,则不存储URL以避免base64数据
|
||||
url: uploadedUrl || '',
|
||||
mime: asset.mime,
|
||||
width: asset.width,
|
||||
height: asset.height,
|
||||
checksum: asset.checksum
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
const db = getDB();
|
||||
const transaction = db.transaction([EDITS_STORE], 'readwrite');
|
||||
const store = transaction.objectStore(EDITS_STORE);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.add(edit);
|
||||
const request = store.add(lightweightEdit);
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 从uploadResults中获取资产的上传后URL
|
||||
* 注意:这个函数需要根据资产在数组中的位置来匹配上传结果
|
||||
* - 输出资产的索引与uploadResults中的索引相对应
|
||||
* - 源资产(参考图像)的索引从outputAssets.length开始
|
||||
*/
|
||||
const getUploadedAssetUrl = (record: Generation | Edit, assetId: string, assetType: 'output' | 'source' | 'mask'): string | null => {
|
||||
if (!record.uploadResults || record.uploadResults.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let assetIndex = -1;
|
||||
|
||||
// 根据资产类型确定在uploadResults中的索引
|
||||
if (assetType === 'output') {
|
||||
// 输出资产的索引与在outputAssets数组中的索引相同
|
||||
assetIndex = record.outputAssets.findIndex(a => a.id === assetId);
|
||||
} else if (assetType === 'source') {
|
||||
// 源资产(参考图像)的索引从outputAssets.length开始
|
||||
assetIndex = record.sourceAssets?.findIndex(a => a.id === assetId);
|
||||
if (assetIndex >= 0) {
|
||||
assetIndex += record.outputAssets.length;
|
||||
}
|
||||
} else if (assetType === 'mask') {
|
||||
// 遮罩参考资产通常是第一个输出资产之后的第一个源资产
|
||||
assetIndex = record.outputAssets.length;
|
||||
}
|
||||
|
||||
// 检查索引是否有效并且对应的上传结果是否存在且成功
|
||||
if (assetIndex >= 0 && assetIndex < record.uploadResults.length) {
|
||||
const uploadResult = record.uploadResults[assetIndex];
|
||||
if (uploadResult.success && uploadResult.url) {
|
||||
return uploadResult.url;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取所有生成记录(按时间倒序)
|
||||
*/
|
||||
@@ -148,18 +268,16 @@ export const getEditsByParentGenerationId = async (parentGenerationId: string):
|
||||
/**
|
||||
* 删除最旧的记录以保持限制
|
||||
*/
|
||||
export const cleanupOldRecords = async (limit: number = 1000): Promise<void> => {
|
||||
export const cleanupOldRecords = async (limit: number = 100): Promise<void> => {
|
||||
const db = getDB();
|
||||
|
||||
// 清理生成记录
|
||||
const genTransaction = db.transaction([GENERATIONS_STORE], 'readwrite');
|
||||
const genStore = genTransaction.objectStore(GENERATIONS_STORE);
|
||||
const genIndex = genStore.index('timestamp');
|
||||
|
||||
// 清理编辑记录
|
||||
const editTransaction = db.transaction([EDITS_STORE], 'readwrite');
|
||||
const editStore = editTransaction.objectStore(EDITS_STORE);
|
||||
const editIndex = editStore.index('timestamp');
|
||||
|
||||
// 获取所有记录并按时间排序
|
||||
const allGenerations = await getAllGenerations();
|
||||
@@ -181,6 +299,117 @@ export const cleanupOldRecords = async (limit: number = 1000): Promise<void> =>
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 清理记录中的base64数据
|
||||
*/
|
||||
export const cleanupBase64Data = async (): Promise<void> => {
|
||||
try {
|
||||
// 获取所有生成记录
|
||||
const generations = await getAllGenerations();
|
||||
|
||||
// 获取所有编辑记录
|
||||
const edits = await getAllEdits();
|
||||
|
||||
const db = getDB();
|
||||
|
||||
// 更新生成记录
|
||||
for (const generation of generations) {
|
||||
// 检查是否有base64数据需要清理
|
||||
let needsUpdate = false;
|
||||
|
||||
// 清理源资产中的base64数据
|
||||
const cleanedSourceAssets = generation.sourceAssets.map((asset: any) => {
|
||||
if (asset.url && asset.url.startsWith('data:')) {
|
||||
needsUpdate = true;
|
||||
return {
|
||||
...asset,
|
||||
url: '' // 移除base64数据
|
||||
};
|
||||
}
|
||||
return asset;
|
||||
});
|
||||
|
||||
// 清理输出资产中的base64数据
|
||||
const cleanedOutputAssets = generation.outputAssets.map((asset: any) => {
|
||||
if (asset.url && asset.url.startsWith('data:')) {
|
||||
needsUpdate = true;
|
||||
return {
|
||||
...asset,
|
||||
url: '' // 移除base64数据
|
||||
};
|
||||
}
|
||||
return asset;
|
||||
});
|
||||
|
||||
// 如果需要更新,则保存清理后的记录
|
||||
if (needsUpdate) {
|
||||
const cleanedGeneration = {
|
||||
...generation,
|
||||
sourceAssets: cleanedSourceAssets,
|
||||
outputAssets: cleanedOutputAssets
|
||||
};
|
||||
|
||||
const transaction = db.transaction([GENERATIONS_STORE], 'readwrite');
|
||||
const store = transaction.objectStore(GENERATIONS_STORE);
|
||||
await new Promise((resolve, reject) => {
|
||||
const request = store.put(cleanedGeneration);
|
||||
request.onsuccess = () => resolve(undefined);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 更新编辑记录
|
||||
for (const edit of edits) {
|
||||
// 检查是否有base64数据需要清理
|
||||
let needsUpdate = false;
|
||||
|
||||
// 清理遮罩参考资产中的base64数据
|
||||
let cleanedMaskReferenceAsset = edit.maskReferenceAsset;
|
||||
if (edit.maskReferenceAsset && edit.maskReferenceAsset.url && edit.maskReferenceAsset.url.startsWith('data:')) {
|
||||
needsUpdate = true;
|
||||
cleanedMaskReferenceAsset = {
|
||||
...edit.maskReferenceAsset,
|
||||
url: '' // 移除base64数据
|
||||
};
|
||||
}
|
||||
|
||||
// 清理输出资产中的base64数据
|
||||
const cleanedOutputAssets = edit.outputAssets.map((asset: any) => {
|
||||
if (asset.url && asset.url.startsWith('data:')) {
|
||||
needsUpdate = true;
|
||||
return {
|
||||
...asset,
|
||||
url: '' // 移除base64数据
|
||||
};
|
||||
}
|
||||
return asset;
|
||||
});
|
||||
|
||||
// 如果需要更新,则保存清理后的记录
|
||||
if (needsUpdate) {
|
||||
const cleanedEdit = {
|
||||
...edit,
|
||||
maskReferenceAsset: cleanedMaskReferenceAsset,
|
||||
outputAssets: cleanedOutputAssets
|
||||
};
|
||||
|
||||
const transaction = db.transaction([EDITS_STORE], 'readwrite');
|
||||
const store = transaction.objectStore(EDITS_STORE);
|
||||
await new Promise((resolve, reject) => {
|
||||
const request = store.put(cleanedEdit);
|
||||
request.onsuccess = () => resolve(undefined);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log('IndexedDB中的base64数据清理完成');
|
||||
} catch (error) {
|
||||
console.error('清理IndexedDB中的base64数据时出错:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 清空所有记录
|
||||
*/
|
||||
@@ -205,4 +434,14 @@ export const clearAllRecords = async (): Promise<void> => {
|
||||
request.onerror = () => reject(request.error);
|
||||
})
|
||||
]).then(() => undefined);
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭数据库连接
|
||||
*/
|
||||
export const closeDB = (): void => {
|
||||
if (db) {
|
||||
db.close();
|
||||
db = null;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user