showDirectoryPicker()兼容

去年开始忽然自开发使用的showDirectoryPicker()这个API选择文件夹上传的功能开始缺失dll文件。找了下原因是Chrome浏览器自动升级导致的。

1. 定位原因

Chrome因为安全策略,dll这种后缀的文件会自动过滤。

目前只发现这种后缀的文件有问题,而且过滤文件用户是无感知的。当文件夹内文件数量几千上万的时候,很容易出错。

2. 解决方案

    1. showDirectoryPicker不再使用,转为不存在兼容问题的input标签的webkitdirectory属性
js 复制代码
                        <input
				type="file"
				multiple
				onChange={handleOpenDir}
				webkitdirectory="true"
                         />
    1. 兼容原来的showDirectoryPicker的后续逻辑

原来获取文件目录结构的代码如下:

js 复制代码
let dir = await window.showDirectoryPicker();

查看MDN文档,了解到API基于FileSystemDirectoryHandle这个类处理的。

如果改为input方案,则获取的数据需要转换为上面的dir结构,这时候就需要模拟一个上面的类和中转方法,讲input获取到的文件列表转化成上面dir结构

js 复制代码
const dir = convertFileListToDirectoryHandle(fileList);

convertFileListToDirectoryHandle方法具体如下:

js 复制代码
function convertFileListToDirectoryHandle(fileList) {
	if (!fileList.length) return null;

	// 取根目录名(以第一个文件路径的第一个部分为准)
	const rootName = fileList[0].webkitRelativePath.split("/")[0];
	const rootHandle = new VirtualDirectoryHandle(rootName);

	for (const file of fileList) {
		const relativePath = file.webkitRelativePath.split("/").slice(1); // 去掉根目录部分
		rootHandle.addItem(relativePath, file);
	}

	return rootHandle;
}

由于不了解FileSystemDirectoryHandle这个类的具体逻辑,将问题抛给AI,chatGPT 或者 Cursor,让他给出一个类,大概率是不能直接用的,然后在这个类的基础上根据自己的需求进行方法和逻辑修改

js 复制代码
class VirtualFileHandle {
	constructor(file) {
		this.file = file;
		this.kind = "file";
		this.name = file.name;
	}

	async getFile() {
		return this.file;
	}
}
class VirtualDirectoryHandle {
	constructor(name) {
		this.name = name;
		this.kind = "directory";
		this.entriesMap = new Map();
	}

	async *entries() {
		for (const [name, handle] of this.entriesMap) {
			yield [name, handle];
		}
	}

	async getFileHandle(name) {
		if (
			this.entriesMap.has(name) &&
			this.entriesMap.get(name).kind === "file"
		) {
			return this.entriesMap.get(name);
		}
		throw new Error("File not found");
	}

	async getDirectoryHandle(name) {
		if (
			this.entriesMap.has(name) &&
			this.entriesMap.get(name).kind === "directory"
		) {
			return this.entriesMap.get(name);
		}
		throw new Error("Directory not found");
	}
	async resolve(handle) {
		const pathParts = [];

		const findPath = (directory, targetHandle, currentPath) => {
			for (const [name, value] of directory.entriesMap) {
				if (
					value === targetHandle ||
					(value instanceof VirtualFileHandle &&
						value.file === targetHandle.file)
				) {
					pathParts.push(...currentPath, name);
					return true;
				}
				if (value instanceof VirtualDirectoryHandle) {
					if (findPath(value, targetHandle, [...currentPath, name])) {
						return true;
					}
				}
			}
			return false;
		};

		if (findPath(this, handle, [])) {
			return pathParts.length > 0 ? pathParts : null;
		}
		return null; // 未找到
	}

	addItem(pathParts, file) {
		let currentDir = this;
		for (let i = 0; i < pathParts.length; i++) {
			const part = pathParts[i];
			if (i === pathParts.length - 1) {
				// 最后一个是文件
				currentDir.entriesMap.set(part, new VirtualFileHandle(file));
			} else {
				// 目录
				if (!currentDir.entriesMap.has(part)) {
					currentDir.entriesMap.set(part, new VirtualDirectoryHandle(part));
				}
				currentDir = currentDir.entriesMap.get(part);
			}
		}
	}
}

export default VirtualDirectoryHandle;
相关推荐
子兮曰5 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰5 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万5 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝5 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋5 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁5 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95275 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大5 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师5 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学5 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端