You've already forked Nano-Banana-AI-Image-Editor
修复内存泄漏问题,优化Blob URL清理机制
This commit is contained in:
@@ -123,6 +123,10 @@ interface AppState {
|
||||
addBlob: (blob: Blob) => string;
|
||||
getBlob: (url: string) => Blob | undefined;
|
||||
cleanupOldHistory: () => void;
|
||||
|
||||
// Blob URL清理操作
|
||||
revokeBlobUrls: (urls: string[]) => void;
|
||||
cleanupAllBlobUrls: () => void;
|
||||
}
|
||||
|
||||
export const useAppStore = create<AppState>()(
|
||||
@@ -314,6 +318,36 @@ export const useAppStore = create<AppState>()(
|
||||
|
||||
// 清理旧记录以保持在限制内(现在限制为1000条)
|
||||
if (updatedProject.generations.length > 1000) {
|
||||
// 收集需要释放的Blob URLs
|
||||
const urlsToRevoke: string[] = [];
|
||||
const generationsToRemove = updatedProject.generations.slice(0, updatedProject.generations.length - 1000);
|
||||
generationsToRemove.forEach(gen => {
|
||||
gen.sourceAssets.forEach(asset => {
|
||||
if (asset.blobUrl.startsWith('blob:')) {
|
||||
urlsToRevoke.push(asset.blobUrl);
|
||||
}
|
||||
});
|
||||
gen.outputAssetsBlobUrls.forEach(url => {
|
||||
if (url.startsWith('blob:')) {
|
||||
urlsToRevoke.push(url);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 释放Blob URLs
|
||||
if (urlsToRevoke.length > 0) {
|
||||
set((innerState) => {
|
||||
urlsToRevoke.forEach(url => {
|
||||
URL.revokeObjectURL(url);
|
||||
const newBlobStore = new Map(innerState.blobStore);
|
||||
newBlobStore.delete(url);
|
||||
innerState = { ...innerState, blobStore: newBlobStore };
|
||||
});
|
||||
return innerState;
|
||||
});
|
||||
}
|
||||
|
||||
// 清理数组
|
||||
updatedProject.generations.splice(0, updatedProject.generations.length - 1000);
|
||||
// 同时清理IndexedDB中的旧记录
|
||||
indexedDBService.cleanupOldRecords(1000).catch(err => {
|
||||
@@ -409,6 +443,34 @@ export const useAppStore = create<AppState>()(
|
||||
|
||||
// 清理旧记录以保持在限制内(现在限制为1000条)
|
||||
if (updatedProject.edits.length > 1000) {
|
||||
// 收集需要释放的Blob URLs
|
||||
const urlsToRevoke: string[] = [];
|
||||
const editsToRemove = updatedProject.edits.slice(0, updatedProject.edits.length - 1000);
|
||||
editsToRemove.forEach(edit => {
|
||||
if (edit.maskReferenceAssetBlobUrl && edit.maskReferenceAssetBlobUrl.startsWith('blob:')) {
|
||||
urlsToRevoke.push(edit.maskReferenceAssetBlobUrl);
|
||||
}
|
||||
edit.outputAssetsBlobUrls.forEach(url => {
|
||||
if (url.startsWith('blob:')) {
|
||||
urlsToRevoke.push(url);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 释放Blob URLs
|
||||
if (urlsToRevoke.length > 0) {
|
||||
set((innerState) => {
|
||||
urlsToRevoke.forEach(url => {
|
||||
URL.revokeObjectURL(url);
|
||||
const newBlobStore = new Map(innerState.blobStore);
|
||||
newBlobStore.delete(url);
|
||||
innerState = { ...innerState, blobStore: newBlobStore };
|
||||
});
|
||||
return innerState;
|
||||
});
|
||||
}
|
||||
|
||||
// 清理数组
|
||||
updatedProject.edits.splice(0, updatedProject.edits.length - 1000);
|
||||
// 同时清理IndexedDB中的旧记录
|
||||
indexedDBService.cleanupOldRecords(1000).catch(err => {
|
||||
@@ -437,16 +499,56 @@ export const useAppStore = create<AppState>()(
|
||||
const generations = [...state.currentProject.generations];
|
||||
const edits = [...state.currentProject.edits];
|
||||
|
||||
// 收集需要释放的Blob URLs
|
||||
const urlsToRevoke: string[] = [];
|
||||
|
||||
// 如果生成记录超过1000条,只保留最新的1000条
|
||||
if (generations.length > 1000) {
|
||||
const generationsToRemove = generations.slice(0, generations.length - 1000);
|
||||
generationsToRemove.forEach(gen => {
|
||||
gen.sourceAssets.forEach(asset => {
|
||||
if (asset.blobUrl.startsWith('blob:')) {
|
||||
urlsToRevoke.push(asset.blobUrl);
|
||||
}
|
||||
});
|
||||
gen.outputAssetsBlobUrls.forEach(url => {
|
||||
if (url.startsWith('blob:')) {
|
||||
urlsToRevoke.push(url);
|
||||
}
|
||||
});
|
||||
});
|
||||
generations.splice(0, generations.length - 1000);
|
||||
}
|
||||
|
||||
// 如果编辑记录超过1000条,只保留最新的1000条
|
||||
if (edits.length > 1000) {
|
||||
const editsToRemove = edits.slice(0, edits.length - 1000);
|
||||
editsToRemove.forEach(edit => {
|
||||
if (edit.maskReferenceAssetBlobUrl && edit.maskReferenceAssetBlobUrl.startsWith('blob:')) {
|
||||
urlsToRevoke.push(edit.maskReferenceAssetBlobUrl);
|
||||
}
|
||||
edit.outputAssetsBlobUrls.forEach(url => {
|
||||
if (url.startsWith('blob:')) {
|
||||
urlsToRevoke.push(url);
|
||||
}
|
||||
});
|
||||
});
|
||||
edits.splice(0, edits.length - 1000);
|
||||
}
|
||||
|
||||
// 释放Blob URLs
|
||||
if (urlsToRevoke.length > 0) {
|
||||
set((innerState) => {
|
||||
urlsToRevoke.forEach(url => {
|
||||
URL.revokeObjectURL(url);
|
||||
const newBlobStore = new Map(innerState.blobStore);
|
||||
newBlobStore.delete(url);
|
||||
innerState = { ...innerState, blobStore: newBlobStore };
|
||||
});
|
||||
return innerState;
|
||||
});
|
||||
}
|
||||
|
||||
// 同时清理IndexedDB中的旧记录
|
||||
indexedDBService.cleanupOldRecords(1000).catch(err => {
|
||||
console.error('清理IndexedDB旧记录失败:', err);
|
||||
@@ -460,6 +562,27 @@ export const useAppStore = create<AppState>()(
|
||||
updatedAt: Date.now()
|
||||
}
|
||||
};
|
||||
}),
|
||||
|
||||
// 释放指定的Blob URLs
|
||||
revokeBlobUrls: (urls: string[]) => set((state) => {
|
||||
urls.forEach(url => {
|
||||
if (state.blobStore.has(url)) {
|
||||
URL.revokeObjectURL(url);
|
||||
const newBlobStore = new Map(state.blobStore);
|
||||
newBlobStore.delete(url);
|
||||
state = { ...state, blobStore: newBlobStore };
|
||||
}
|
||||
});
|
||||
return state;
|
||||
}),
|
||||
|
||||
// 释放所有Blob URLs
|
||||
cleanupAllBlobUrls: () => set((state) => {
|
||||
state.blobStore.forEach((_, url) => {
|
||||
URL.revokeObjectURL(url);
|
||||
});
|
||||
return { ...state, blobStore: new Map() };
|
||||
})
|
||||
}),
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user