Add hierarchical file picker
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Рекурсивно получить из ZIP только файлы с разрешёнными расширениями.
|
||||
// Построить дерево ZIP с файлами только разрешённых расширений.
|
||||
|
||||
function extensionAllowed(name, allowedExt) {
|
||||
const lowerName = name.toLowerCase();
|
||||
@@ -9,24 +9,54 @@ function makeFile(data, name) {
|
||||
return new File([data], name);
|
||||
}
|
||||
|
||||
async function listEntries(data, prefix, allowedExt, depth) {
|
||||
function node(kind, name, path, children = [], file = null) {
|
||||
return { id: crypto.randomUUID(), kind, name, path, children, file, expanded: true };
|
||||
}
|
||||
|
||||
function rebaseTree(root, prefix) {
|
||||
root.path = `${prefix}/${root.path}`;
|
||||
if (root.file) root.file = new File([root.file], root.path, { lastModified: root.file.lastModified });
|
||||
root.children.forEach((child) => rebaseTree(child, prefix));
|
||||
return root;
|
||||
}
|
||||
|
||||
function addPath(root, parts, fileNode) {
|
||||
let current = root;
|
||||
parts.forEach((part, index) => {
|
||||
const last = index === parts.length - 1;
|
||||
let child = current.children.find((item) => item.name === part);
|
||||
if (!child) {
|
||||
child = last
|
||||
? fileNode
|
||||
: node('folder', part, `${current.path}/${part}`);
|
||||
current.children.push(child);
|
||||
}
|
||||
current = child;
|
||||
});
|
||||
}
|
||||
|
||||
async function listEntries(data, zipName, allowedExt, depth) {
|
||||
if (depth > 20) throw new Error('Слишком глубокая вложенность ZIP');
|
||||
const entries = fflate.unzipSync(data);
|
||||
const files = [];
|
||||
const root = node('zip', zipName, zipName);
|
||||
|
||||
for (const [entryName, entryData] of Object.entries(entries)) {
|
||||
if (entryName.endsWith('/')) continue;
|
||||
const path = prefix ? `${prefix}/${entryName}` : entryName;
|
||||
if (entryName.toLowerCase().endsWith('.zip')) {
|
||||
files.push(...await listEntries(entryData, path, allowedExt, depth + 1));
|
||||
const nested = await listEntries(entryData, entryName, allowedExt, depth + 1);
|
||||
rebaseTree(nested, zipName);
|
||||
nested.name = entryName.split('/').pop();
|
||||
addPath(root, entryName.split('/'), nested);
|
||||
} else if (extensionAllowed(entryName, allowedExt)) {
|
||||
files.push(makeFile(entryData, path));
|
||||
const path = `${zipName}/${entryName}`;
|
||||
const file = makeFile(entryData, path);
|
||||
addPath(root, entryName.split('/'), node('file', entryName.split('/').pop(), path, [], file));
|
||||
}
|
||||
}
|
||||
return files;
|
||||
return root;
|
||||
}
|
||||
|
||||
export async function listZipFiles(file, allowedExt) {
|
||||
const data = new Uint8Array(await file.arrayBuffer());
|
||||
return listEntries(data, '', allowedExt, 0);
|
||||
return listEntries(data, file.name, allowedExt, 0);
|
||||
}
|
||||
Reference in New Issue
Block a user