changed frontend to download export file in chunks

This commit is contained in:
juhanikat
2025-07-03 15:54:19 +03:00
parent c3682e44c8
commit d71fa27ced
3 changed files with 49 additions and 29 deletions
@@ -9,46 +9,65 @@ import { currentDateAsString } from '@/shared/currentDateAsString'
export const CrossSearchExportMenuItem = ({ handleClose }: { handleClose: () => void }) => { export const CrossSearchExportMenuItem = ({ handleClose }: { handleClose: () => void }) => {
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const { sqlColumnFilters, sqlOrderBy } = usePageContext() const { sqlColumnFilters, sqlOrderBy } = usePageContext()
const notify = useNotify() const { notify, setMessage } = useNotify()
const token = useUser().token const token = useUser().token
const fetchOptions = token ? { headers: { Authorization: `Bearer ${token}` } } : {} const fetchOptions = token ? { headers: { Authorization: `Bearer ${token}` } } : {}
const filename = `cross_search${currentDateAsString()}.csv` const filename = `cross_search${currentDateAsString()}.csv`
const fetchCSVFile = () => { const fetchCSVFile = async () => {
setLoading(true) setLoading(true)
notify('Downloading CSV file, please wait...', 'info', null)
const URIColumnFilters = encodeURIComponent(JSON.stringify(sqlColumnFilters)) const URIColumnFilters = encodeURIComponent(JSON.stringify(sqlColumnFilters))
const URIOrderBy = encodeURIComponent(JSON.stringify(sqlOrderBy)) const URIOrderBy = encodeURIComponent(JSON.stringify(sqlOrderBy))
fetch(`${BACKEND_URL}/crosssearch/export/${URIColumnFilters}/${URIOrderBy}`, fetchOptions) notify('Generating CSV file, please wait...', 'info', null)
.then(response => { const response = await fetch(`${BACKEND_URL}/crosssearch/export/${URIColumnFilters}/${URIOrderBy}`, fetchOptions)
if (!response.ok) {
throw new Error('Server response was not OK.') if (!response.ok) {
} throw new Error('Server response was not OK.')
return response.blob() }
}) const reader = response.body!.getReader()
.then(blob => { const file: Uint8Array<ArrayBufferLike>[] = []
const blobUrl = window.URL.createObjectURL(blob) let bytes = 0
const downloadLink = document.createElement('a') let closed = false
downloadLink.href = blobUrl
downloadLink.download = filename const showDownloadProgress = () => {
document.body.appendChild(downloadLink) if (!closed) {
downloadLink.click() setTimeout(() => {
downloadLink.remove() setMessage(`Downloading CSV file, ${Math.round((bytes / 1000000) * 10) / 10} MB`)
window.URL.revokeObjectURL(blobUrl) showDownloadProgress()
notify('Download finished.') }, 500)
}) }
.catch(_ => { }
notify('Downloading cross-search export failed.', 'error') showDownloadProgress()
}) while (true) {
.finally(() => setLoading(false)) const { done, value } = await reader.read()
if (done) break
bytes = bytes + value.length
file.push(value)
}
closed = true
try {
const blobUrl = window.URL.createObjectURL(new Blob(file))
const downloadLink = document.createElement('a')
downloadLink.href = blobUrl
downloadLink.download = filename
document.body.appendChild(downloadLink)
downloadLink.click()
downloadLink.remove()
window.URL.revokeObjectURL(blobUrl)
notify('Download finished.')
} catch {
notify('Downloading cross-search export failed.', 'error')
} finally {
setLoading(false)
}
} }
return ( return (
<MenuItem <MenuItem
onClick={() => { onClick={() => {
fetchCSVFile() void fetchCSVFile()
handleClose() handleClose()
}} }}
disabled={loading} disabled={loading}
+2 -1
View File
@@ -8,6 +8,7 @@ export type NotificationContext = {
open: boolean open: boolean
setOpen: (open: boolean) => void setOpen: (open: boolean) => void
message: string message: string
setMessage: (message: string) => void
severity?: Severity severity?: Severity
timeoutValue?: number | null timeoutValue?: number | null
} }
@@ -36,7 +37,7 @@ export const NotificationContextProvider = ({ children }: { children: ReactNode
}, 200) }, 200)
} }
return ( return (
<NotificationContext.Provider value={{ open, setOpen, message, severity, timeoutValue, notify }}> <NotificationContext.Provider value={{ open, setOpen, message, setMessage, severity, timeoutValue, notify }}>
{children} {children}
</NotificationContext.Provider> </NotificationContext.Provider>
) )
+2 -2
View File
@@ -2,6 +2,6 @@ import { NotificationContext } from '@/components/Notification'
import { useContext } from 'react' import { useContext } from 'react'
export const useNotify = () => { export const useNotify = () => {
const { notify } = useContext(NotificationContext) const { notify, setMessage } = useContext(NotificationContext)
return notify return { notify, setMessage }
} }