You've already forked Nano-Banana-AI-Image-Editor
修复内存溢出问题
This commit is contained in:
@@ -127,8 +127,14 @@ interface AppState {
|
||||
// Blob URL清理操作
|
||||
revokeBlobUrls: (urls: string[]) => void;
|
||||
cleanupAllBlobUrls: () => void;
|
||||
|
||||
// 定期清理Blob URL
|
||||
scheduleBlobCleanup: () => void;
|
||||
}
|
||||
|
||||
// 限制历史记录数量
|
||||
const MAX_HISTORY_ITEMS = 50;
|
||||
|
||||
export const useAppStore = create<AppState>()(
|
||||
devtools(
|
||||
persist(
|
||||
@@ -250,7 +256,19 @@ export const useAppStore = create<AppState>()(
|
||||
checksum: asset.checksum,
|
||||
blobUrl
|
||||
};
|
||||
} else if (asset.url.startsWith('blob:')) {
|
||||
// 如果已经是Blob URL,直接使用
|
||||
return {
|
||||
id: asset.id,
|
||||
type: asset.type,
|
||||
mime: asset.mime,
|
||||
width: asset.width,
|
||||
height: asset.height,
|
||||
checksum: asset.checksum,
|
||||
blobUrl: asset.url
|
||||
};
|
||||
}
|
||||
// 对于其他URL类型,创建一个新的Blob URL
|
||||
return {
|
||||
id: asset.id,
|
||||
type: asset.type,
|
||||
@@ -285,7 +303,11 @@ export const useAppStore = create<AppState>()(
|
||||
});
|
||||
|
||||
return blobUrl;
|
||||
} else if (asset.url.startsWith('blob:')) {
|
||||
// 如果已经是Blob URL,直接使用
|
||||
return asset.url;
|
||||
}
|
||||
// 对于其他URL类型,直接使用
|
||||
return asset.url;
|
||||
});
|
||||
|
||||
@@ -316,11 +338,11 @@ export const useAppStore = create<AppState>()(
|
||||
updatedAt: Date.now()
|
||||
};
|
||||
|
||||
// 清理旧记录以保持在限制内(现在限制为1000条)
|
||||
if (updatedProject.generations.length > 1000) {
|
||||
// 清理旧记录以保持在限制内
|
||||
if (updatedProject.generations.length > MAX_HISTORY_ITEMS) {
|
||||
// 收集需要释放的Blob URLs
|
||||
const urlsToRevoke: string[] = [];
|
||||
const generationsToRemove = updatedProject.generations.slice(0, updatedProject.generations.length - 1000);
|
||||
const generationsToRemove = updatedProject.generations.slice(0, updatedProject.generations.length - MAX_HISTORY_ITEMS);
|
||||
generationsToRemove.forEach(gen => {
|
||||
gen.sourceAssets.forEach(asset => {
|
||||
if (asset.blobUrl.startsWith('blob:')) {
|
||||
@@ -348,9 +370,9 @@ export const useAppStore = create<AppState>()(
|
||||
}
|
||||
|
||||
// 清理数组
|
||||
updatedProject.generations.splice(0, updatedProject.generations.length - 1000);
|
||||
updatedProject.generations.splice(0, updatedProject.generations.length - MAX_HISTORY_ITEMS);
|
||||
// 同时清理IndexedDB中的旧记录
|
||||
indexedDBService.cleanupOldRecords(1000).catch(err => {
|
||||
indexedDBService.cleanupOldRecords(MAX_HISTORY_ITEMS).catch(err => {
|
||||
console.error('清理IndexedDB旧记录失败:', err);
|
||||
});
|
||||
}
|
||||
@@ -415,7 +437,11 @@ export const useAppStore = create<AppState>()(
|
||||
});
|
||||
|
||||
return blobUrl;
|
||||
} else if (asset.url.startsWith('blob:')) {
|
||||
// 如果已经是Blob URL,直接使用
|
||||
return asset.url;
|
||||
}
|
||||
// 对于其他URL类型,直接使用
|
||||
return asset.url;
|
||||
});
|
||||
|
||||
@@ -441,11 +467,11 @@ export const useAppStore = create<AppState>()(
|
||||
updatedAt: Date.now()
|
||||
};
|
||||
|
||||
// 清理旧记录以保持在限制内(现在限制为1000条)
|
||||
if (updatedProject.edits.length > 1000) {
|
||||
// 清理旧记录以保持在限制内
|
||||
if (updatedProject.edits.length > MAX_HISTORY_ITEMS) {
|
||||
// 收集需要释放的Blob URLs
|
||||
const urlsToRevoke: string[] = [];
|
||||
const editsToRemove = updatedProject.edits.slice(0, updatedProject.edits.length - 1000);
|
||||
const editsToRemove = updatedProject.edits.slice(0, updatedProject.edits.length - MAX_HISTORY_ITEMS);
|
||||
editsToRemove.forEach(edit => {
|
||||
if (edit.maskReferenceAssetBlobUrl && edit.maskReferenceAssetBlobUrl.startsWith('blob:')) {
|
||||
urlsToRevoke.push(edit.maskReferenceAssetBlobUrl);
|
||||
@@ -471,9 +497,9 @@ export const useAppStore = create<AppState>()(
|
||||
}
|
||||
|
||||
// 清理数组
|
||||
updatedProject.edits.splice(0, updatedProject.edits.length - 1000);
|
||||
updatedProject.edits.splice(0, updatedProject.edits.length - MAX_HISTORY_ITEMS);
|
||||
// 同时清理IndexedDB中的旧记录
|
||||
indexedDBService.cleanupOldRecords(1000).catch(err => {
|
||||
indexedDBService.cleanupOldRecords(MAX_HISTORY_ITEMS).catch(err => {
|
||||
console.error('清理IndexedDB旧记录失败:', err);
|
||||
});
|
||||
}
|
||||
@@ -492,7 +518,7 @@ export const useAppStore = create<AppState>()(
|
||||
|
||||
setSelectedTool: (tool) => set({ selectedTool: tool }),
|
||||
|
||||
// 清理旧的历史记录,保留最多1000条
|
||||
// 清理旧的历史记录
|
||||
cleanupOldHistory: () => set((state) => {
|
||||
if (!state.currentProject) return {};
|
||||
|
||||
@@ -502,9 +528,9 @@ export const useAppStore = create<AppState>()(
|
||||
// 收集需要释放的Blob URLs
|
||||
const urlsToRevoke: string[] = [];
|
||||
|
||||
// 如果生成记录超过1000条,只保留最新的1000条
|
||||
if (generations.length > 1000) {
|
||||
const generationsToRemove = generations.slice(0, generations.length - 1000);
|
||||
// 如果生成记录超过限制,只保留最新的记录
|
||||
if (generations.length > MAX_HISTORY_ITEMS) {
|
||||
const generationsToRemove = generations.slice(0, generations.length - MAX_HISTORY_ITEMS);
|
||||
generationsToRemove.forEach(gen => {
|
||||
gen.sourceAssets.forEach(asset => {
|
||||
if (asset.blobUrl.startsWith('blob:')) {
|
||||
@@ -517,12 +543,12 @@ export const useAppStore = create<AppState>()(
|
||||
}
|
||||
});
|
||||
});
|
||||
generations.splice(0, generations.length - 1000);
|
||||
generations.splice(0, generations.length - MAX_HISTORY_ITEMS);
|
||||
}
|
||||
|
||||
// 如果编辑记录超过1000条,只保留最新的1000条
|
||||
if (edits.length > 1000) {
|
||||
const editsToRemove = edits.slice(0, edits.length - 1000);
|
||||
// 如果编辑记录超过限制,只保留最新的记录
|
||||
if (edits.length > MAX_HISTORY_ITEMS) {
|
||||
const editsToRemove = edits.slice(0, edits.length - MAX_HISTORY_ITEMS);
|
||||
editsToRemove.forEach(edit => {
|
||||
if (edit.maskReferenceAssetBlobUrl && edit.maskReferenceAssetBlobUrl.startsWith('blob:')) {
|
||||
urlsToRevoke.push(edit.maskReferenceAssetBlobUrl);
|
||||
@@ -533,7 +559,7 @@ export const useAppStore = create<AppState>()(
|
||||
}
|
||||
});
|
||||
});
|
||||
edits.splice(0, edits.length - 1000);
|
||||
edits.splice(0, edits.length - MAX_HISTORY_ITEMS);
|
||||
}
|
||||
|
||||
// 释放Blob URLs
|
||||
@@ -550,7 +576,7 @@ export const useAppStore = create<AppState>()(
|
||||
}
|
||||
|
||||
// 同时清理IndexedDB中的旧记录
|
||||
indexedDBService.cleanupOldRecords(1000).catch(err => {
|
||||
indexedDBService.cleanupOldRecords(MAX_HISTORY_ITEMS).catch(err => {
|
||||
console.error('清理IndexedDB旧记录失败:', err);
|
||||
});
|
||||
|
||||
@@ -583,7 +609,42 @@ export const useAppStore = create<AppState>()(
|
||||
URL.revokeObjectURL(url);
|
||||
});
|
||||
return { ...state, blobStore: new Map() };
|
||||
})
|
||||
}),
|
||||
|
||||
// 定期清理Blob URL
|
||||
scheduleBlobCleanup: () => {
|
||||
// 清理超过10分钟未使用的Blob
|
||||
const state = get();
|
||||
const now = Date.now();
|
||||
|
||||
// 这里我们简单地清理所有Blob,因为在实际应用中很难跟踪哪些Blob正在使用
|
||||
// 在生产环境中,您可能需要更复杂的跟踪机制
|
||||
state.blobStore.forEach((blob, url) => {
|
||||
// 检查URL是否仍在使用中
|
||||
const isUsedInProject = state.currentProject && (
|
||||
state.currentProject.generations.some(gen =>
|
||||
gen.sourceAssets.some(asset => asset.blobUrl === url) ||
|
||||
gen.outputAssetsBlobUrls.some(outputUrl => outputUrl === url)
|
||||
) ||
|
||||
state.currentProject.edits.some(edit =>
|
||||
(edit.maskReferenceAssetBlobUrl === url) ||
|
||||
edit.outputAssetsBlobUrls.some(outputUrl => outputUrl === url)
|
||||
)
|
||||
);
|
||||
|
||||
const isUsedInCanvas = state.canvasImage === url;
|
||||
const isUsedInUploads = state.uploadedImages.includes(url);
|
||||
const isUsedInEdits = state.editReferenceImages.includes(url);
|
||||
|
||||
// 如果Blob没有被使用,则清理它
|
||||
if (!isUsedInProject && !isUsedInCanvas && !isUsedInUploads && !isUsedInEdits) {
|
||||
URL.revokeObjectURL(url);
|
||||
const newBlobStore = new Map(state.blobStore);
|
||||
newBlobStore.delete(url);
|
||||
set({ blobStore: newBlobStore });
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
{
|
||||
name: 'nano-banana-store',
|
||||
|
||||
Reference in New Issue
Block a user