add folder download utils

This commit is contained in:
myl7 2021-11-23 02:13:11 +08:00
parent 8e127d50e6
commit f6d44b7ee5
No known key found for this signature in database
GPG key ID: 04F1013B67177C88
2 changed files with 67 additions and 1 deletions

View file

@ -12,7 +12,7 @@ import dynamic from 'next/dynamic'
import { getExtension, getFileIcon, hasKey } from '../utils/getFileIcon'
import { extensions, preview } from '../utils/getPreviewType'
import { getBaseUrl, downloadMultipleFiles, useProtectedSWRInfinite } from '../utils/tools'
import { getBaseUrl, treeList, downloadMultipleFiles, useProtectedSWRInfinite } from '../utils/tools'
import { VideoPreview } from './previews/VideoPreview'
import { AudioPreview } from './previews/AudioPreview'
@ -285,6 +285,13 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
}
}
// Folder recursive download
const handleFolderDownload = (path: string) => async () => {
for await (const { meta: c, path: p } of treeList(path)) {
console.log(p, c)
}
}
return (
<div className="dark:bg-gray-900 dark:text-gray-100 bg-white rounded shadow">
<div className="dark:border-gray-700 grid items-center grid-cols-12 px-3 space-x-2 border-b border-gray-200">
@ -400,6 +407,13 @@ const FileListing: FunctionComponent<{ query?: ParsedUrlQuery }> = ({ query }) =
>
<FontAwesomeIcon icon={['far', 'copy']} />
</span>
<span
title="Download folder"
className="hover:bg-gray-300 dark:hover:bg-gray-600 p-2 rounded cursor-pointer"
onClick={handleFolderDownload(`${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`)}
>
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} />
</span>
</div>
) : (
<div className="md:flex dark:text-gray-400 hidden p-1 text-gray-700">

View file

@ -156,3 +156,55 @@ export const downloadMultipleFiles = async (files: { name: string; url: string }
window.URL.revokeObjectURL(bUrl)
el.remove()
}
// One-shot recursive tree-like listing for the folder.
// Due to react hook limit, we cannot reuse SWR utils for recursive listing.
export async function* treeList(path: string) {
const hashedToken = getStoredToken(path)
const root = new PathNode(path)
const loader = async (path: string) => {
const data: any = await fetcher(`/api?path=${path}`, hashedToken ?? undefined)
if (data && data.folder) {
console.log(data.folder)
const children = data.folder.value.map(c => {
const p = `${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`
return c.folder ? new PathNode(p) : new PathNode(p, false, c)
})
return { children }
} else {
throw new Error('Path is not folder')
}
}
for await (const { meta: c, path: p } of root.dfs(loader)) {
if (c) {
yield { meta: c, path: p }
}
}
}
// Traverse helper
class PathNode {
private _path: string
private _meta: any
private _isFolder: boolean
constructor(path: string, isFolder?: boolean, meta?: any) {
this._path = path
this._meta = meta
this._isFolder = isFolder ?? true
}
async* dfs(loader: (path: string) => Promise<{ meta?: any, children: PathNode[] }>) {
const ancestors = [this as PathNode]
while (ancestors.length > 0) {
const next = ancestors.pop()!
if (next._isFolder) {
const { meta, children } = await loader(next._path)
ancestors.push(...children)
yield { path: next._path, meta: meta }
} else {
yield { path: next._path, meta: next._meta }
}
}
}
}