@@ -35,6 +35,7 @@ import { useObject } from "@/hooks/use-object"
3535import { useBucket } from "@/hooks/use-bucket"
3636import { useLocalStorage } from "@/hooks/use-local-storage"
3737import { usePermissions } from "@/hooks/use-permissions"
38+ import { useApi } from "@/contexts/api-context"
3839import { useMessage } from "@/lib/feedback/message"
3940import { exportFile } from "@/lib/export-file"
4041import { getContentType } from "@/lib/mime-types"
@@ -56,12 +57,15 @@ import {
5657 shouldShowDeleteAllVersions ,
5758 type BucketVersioningState ,
5859} from "@/lib/object-delete"
60+ import {
61+ buildObjectZipDownloadPayload ,
62+ normalizeObjectZipDownloadUrl ,
63+ type CreateObjectZipDownloadResponse ,
64+ } from "@/lib/object-zip-download"
5965import { TaskStatsButton } from "@/components/tasks/stats-button"
6066import { useAddDeleteKeys , useAddDeleteFolder , useTasks } from "@/contexts/task-context"
6167import type { ColumnDef } from "@tanstack/react-table"
6268import dayjs from "dayjs"
63- import { saveAs } from "file-saver"
64- import JSZip from "jszip"
6569
6670interface ObjectRow {
6771 Key : string
@@ -95,7 +99,8 @@ export function ObjectList({
9599} : ObjectListProps ) {
96100 const { t } = useTranslation ( )
97101 const message = useMessage ( )
98- const { listObject, getSignedUrl, mapAllFiles } = useObject ( bucket )
102+ const api = useApi ( )
103+ const { listObject, getSignedUrl } = useObject ( bucket )
99104 const { getBucketVersioning } = useBucket ( )
100105 const { canCapability } = usePermissions ( )
101106 const addDeleteKeys = useAddDeleteKeys ( )
@@ -416,12 +421,6 @@ export function ObjectList({
416421
417422 const checkedKeys = selectedRowIds
418423
419- const computeRelativeKey = ( key : string ) => {
420- if ( ! key ) return ""
421- const base = prefix || ""
422- return base && key . startsWith ( base ) ? key . slice ( base . length ) : key
423- }
424-
425424 const downloadMultiple = async ( ) => {
426425 if ( ! checkedKeys . length ) {
427426 message . warning ( t ( "Please select at least one item" ) )
@@ -434,74 +433,28 @@ export function ObjectList({
434433 return
435434 }
436435
437- const collecting = message . loading ( t ( "Collecting files" ) , { duration : 0 } )
438- const allFiles : { key : string ; relative : string } [ ] = [ ]
436+ const loadingMsg = message . loading ( t ( "Preparing download" ) , { duration : 0 } )
439437
440438 try {
441- for ( const item of selectedRows ) {
442- if ( item . type === "object" ) {
443- allFiles . push ( {
444- key : item . Key ,
445- relative : computeRelativeKey ( item . Key ) ,
446- } )
447- } else if ( item . type === "prefix" ) {
448- await mapAllFiles ( bucket , item . Key , ( fileKey ) => {
449- allFiles . push ( {
450- key : fileKey ,
451- relative : computeRelativeKey ( fileKey ) ,
452- } )
453- } )
454- }
455- }
456- } catch ( err ) {
457- collecting . destroy ( )
458- message . error ( ( err as Error ) ?. message ?? t ( "Download Failed" ) )
459- return
460- }
461-
462- collecting . destroy ( )
463-
464- if ( ! allFiles . length ) {
465- message . warning ( t ( "No files to download" ) )
466- return
467- }
468-
469- const zip = new JSZip ( )
470- let finished = 0
471- const destroyRef : { current : ( ( ) => void ) | null } = { current : null }
472-
473- const updateProgress = ( ) => {
474- if ( destroyRef . current ) destroyRef . current ( )
475- const handle = message . loading ( `${ t ( "Downloading files" ) } ${ Math . round ( ( finished / allFiles . length ) * 100 ) } %` , {
476- duration : 0 ,
439+ const payload = buildObjectZipDownloadPayload ( {
440+ bucket,
441+ basePrefix : prefix ,
442+ rows : selectedRows ,
477443 } )
478- destroyRef . current = handle . destroy
479- }
444+ const response = ( await api . post ( "/object-zip-downloads" , payload ) ) as CreateObjectZipDownloadResponse
480445
481- updateProgress ( )
446+ if ( ! response . download_url ) {
447+ throw new Error ( t ( "Download Failed" ) )
448+ }
482449
483- try {
484- await Promise . all (
485- allFiles . map ( async ( { key, relative } ) => {
486- const url = await getSignedUrl ( key )
487- const response = await fetch ( url )
488- const blob = await response . blob ( )
489- zip . file ( relative || key , blob )
490- finished ++
491- updateProgress ( )
492- } ) ,
493- )
450+ window . location . href = normalizeObjectZipDownloadUrl ( response . download_url , api . resolveUrl ( "/" ) )
451+ table . resetRowSelection ( )
452+ message . success ( t ( "Download ready" ) )
494453 } catch ( err ) {
495- if ( destroyRef . current ) destroyRef . current ( )
496454 message . error ( ( err as Error ) ?. message ?? t ( "Download Failed" ) )
497- return
455+ } finally {
456+ loadingMsg . destroy ( )
498457 }
499-
500- if ( destroyRef . current ) destroyRef . current ( )
501-
502- const content = await zip . generateAsync ( { type : "blob" } )
503- saveAs ( content , `${ bucket ?? "download" } .zip` )
504- message . success ( t ( "Download ready" ) )
505458 }
506459
507460 const openDeleteDialog = ( keys : string [ ] ) => {
0 commit comments