From 1427e08cb4b3bca1eac21276373e3e58eacd8033 Mon Sep 17 00:00:00 2001 From: twwu Date: Mon, 8 Sep 2025 11:43:32 +0800 Subject: [PATCH] fix: prevent memory leak by revoking object URLs after file download in various components --- web/app/components/app-sidebar/app-info.tsx | 4 +++- .../app-sidebar/dataset-info/dropdown.tsx | 4 +++- .../app/annotation/header-opts/index.tsx | 4 +++- web/app/components/apps/app-card.tsx | 4 +++- .../datasets/list/dataset-card/index.tsx | 5 ++++- .../datasets/list/dataset-card/operations.tsx | 14 +++++++++----- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/web/app/components/app-sidebar/app-info.tsx b/web/app/components/app-sidebar/app-info.tsx index 900cd0619a..d5e7c40744 100644 --- a/web/app/components/app-sidebar/app-info.tsx +++ b/web/app/components/app-sidebar/app-info.tsx @@ -143,9 +143,11 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx }) const a = document.createElement('a') const file = new Blob([data], { type: 'application/yaml' }) - a.href = URL.createObjectURL(file) + const url = URL.createObjectURL(file) + a.href = url a.download = `${appDetail.name}.yml` a.click() + URL.revokeObjectURL(url) } catch { notify({ type: 'error', message: t('app.exportFailed') }) diff --git a/web/app/components/app-sidebar/dataset-info/dropdown.tsx b/web/app/components/app-sidebar/dataset-info/dropdown.tsx index 25967cd039..b9d29f935d 100644 --- a/web/app/components/app-sidebar/dataset-info/dropdown.tsx +++ b/web/app/components/app-sidebar/dataset-info/dropdown.tsx @@ -65,9 +65,11 @@ const DropDown = ({ }) const a = document.createElement('a') const file = new Blob([data], { type: 'application/yaml' }) - a.href = URL.createObjectURL(file) + const url = URL.createObjectURL(file) + a.href = url a.download = `${name}.pipeline` a.click() + URL.revokeObjectURL(url) } catch { Toast.notify({ type: 'error', message: t('app.exportFailed') }) diff --git a/web/app/components/app/annotation/header-opts/index.tsx b/web/app/components/app/annotation/header-opts/index.tsx index 7347caa2f9..8c0ae37c8e 100644 --- a/web/app/components/app/annotation/header-opts/index.tsx +++ b/web/app/components/app/annotation/header-opts/index.tsx @@ -60,9 +60,11 @@ const HeaderOptions: FC = ({ const a = document.createElement('a') const content = listTransformer(list).join('\n') const file = new Blob([content], { type: 'application/jsonl' }) - a.href = URL.createObjectURL(file) + const url = URL.createObjectURL(file) + a.href = url a.download = `annotations-${locale}.jsonl` a.click() + URL.revokeObjectURL(url) } const fetchList = async () => { diff --git a/web/app/components/apps/app-card.tsx b/web/app/components/apps/app-card.tsx index ee9230af12..174404820b 100644 --- a/web/app/components/apps/app-card.tsx +++ b/web/app/components/apps/app-card.tsx @@ -159,9 +159,11 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { }) const a = document.createElement('a') const file = new Blob([data], { type: 'application/yaml' }) - a.href = URL.createObjectURL(file) + const url = URL.createObjectURL(file) + a.href = url a.download = `${app.name}.yml` a.click() + URL.revokeObjectURL(url) } catch { notify({ type: 'error', message: t('app.exportFailed') }) diff --git a/web/app/components/datasets/list/dataset-card/index.tsx b/web/app/components/datasets/list/dataset-card/index.tsx index 253d397d95..f95e7b2199 100644 --- a/web/app/components/datasets/list/dataset-card/index.tsx +++ b/web/app/components/datasets/list/dataset-card/index.tsx @@ -114,9 +114,11 @@ const DatasetCard = ({ }) const a = document.createElement('a') const file = new Blob([data], { type: 'application/yaml' }) - a.href = URL.createObjectURL(file) + const url = URL.createObjectURL(file) + a.href = url a.download = `${name}.pipeline` a.click() + URL.revokeObjectURL(url) } catch { Toast.notify({ type: 'error', message: t('app.exportFailed') }) @@ -274,6 +276,7 @@ const DatasetCard = ({ htmlContent={ void handleExportPipeline: () => void detectIsUsedByApp: () => void @@ -13,6 +14,7 @@ type OperationsProps = { const Operations = ({ showDelete, + showExportPipeline, openRenameModal, handleExportPipeline, detectIsUsedByApp, @@ -27,11 +29,13 @@ const Operations = ({ name={t('common.operation.edit')} handleClick={openRenameModal} /> - + {showExportPipeline && ( + + )} {showDelete && ( <>