LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

前端获取本地文件目录内容

zhenglin
2026年2月7日 11:25 本文热度 66

前端获取本地文件目录内容

一、核心原理说明

由于浏览器的 “沙箱安全机制”,前端 JavaScript 无法直接访问本地文件系统,必须通过用户主动授权(如选择目录操作)才能获取文件目录内容。目前主流实现方案基于两种 API:传统 File API(兼容性优先)和现代 FileSystem Access API(功能优先),以下将详细介绍两种方案的实现流程、代码示例及适用场景。


二、方案一:基于 File API 实现(兼容性首选)

1. 方案概述

通过隐藏的 <input type="file"> 标签(配置 webkitdirectorydirectory 属性)触发用户选择目录操作,用户选择后通过 files 属性获取目录下所有文件的元数据(如文件名、大小、相对路径等)。该方案兼容几乎所有现代浏览器(包括 Chrome、Firefox、Safari 等),但仅支持 “一次性获取选中目录内容”,无法递归遍历子目录或修改文件。

2. 完整使用示例

2.1 HTML 结构(含 UI 交互区)

<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <title>File API 目录访问示例</title>

    <!-- 引入 Tailwind 简化样式(也可自定义 CSS) -->

    <script src="https://cdn.tailwindcss.com"></script>

    <style>

        .file-item { display: flex; align-items: center; padding: 8px; border-bottom: 1px solid #eee; }

        .file-icon { margin-right: 8px; font-size: 18px; }

        .file-info { flex: 1; }

        .file-size { color: #666; }

    </style>

</head>

<body class="p-8 bg-gray-50">

    <div class="max-w-4xl mx-auto bg-white p-6 rounded-lg shadow">

        <h2 class="text-2xl font-bold mb-4">File API 目录内容获取</h2>

        <!-- 触发按钮(隐藏原生 input) -->

        <button id="selectDirBtn" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">

            选择本地目录

        </button>

        <input type="file" id="dirInput" webkitdirectory directory style="display: none;">

        

        <!-- 文件列表展示区 -->

        <div class="mt-4 border rounded-lg max-h-80 overflow-y-auto">

            <div id="fileList" class="p-4 text-center text-gray-500">

                请选择目录以查看文件列表

            </div>

        </div>

    </div>


    <script>

        // 2.2 JavaScript 逻辑实现

        const dirInput = document.getElementById('dirInput');

        const selectDirBtn = document.getElementById('selectDirBtn');

        const fileList = document.getElementById('fileList');


        // 1. 点击按钮触发原生 input 选择目录

        selectDirBtn.addEventListener('click', () => {

            dirInput.click();

        });


        // 2. 监听目录选择变化,处理文件数据

        dirInput.addEventListener('change', (e) => {

            const selectedFiles = e.target.files; // 获取选中目录下的所有文件(含子目录文件)

            if (selectedFiles.length === 0) {

                fileList.innerHTML = '<div class="p-4 text-center text-gray-500">未选择任何文件</div>';

                return;

            }

            

            // 3. 解析文件数据并渲染到页面

            let fileHtml = '';

            Array.from(selectedFiles).forEach(file => {

                // 判断是否为目录(通过 type 为空且 size 为 0 间接判断)

                const isDir = file.type === '' && file.size === 0;

                // 获取文件在目录中的相对路径(webkitRelativePath 为非标准属性,但主流浏览器支持)

                const relativePath = file.webkitRelativePath || file.name;

                // 格式化文件大小(辅助函数)

                const fileSize = isDir ? '—' : formatFileSize(file.size);


                fileHtml += `

                    <div class="file-item">

                        <span class="file-icon ${isDir ? 'text-yellow-500' : 'text-gray-400'}">

                            ${isDir ? '📁' : '📄'}

                        </span>

                        <div class="file-info">

                            <div class="font-medium">${file.name}</div>

                            <div class="text-xs text-gray-500">${relativePath}</div>

                        </div>

                        <div class="file-size text-sm">${fileSize}</div>

                    </div>

                `;

            });


            fileList.innerHTML = fileHtml;

        });


        // 辅助函数:格式化文件大小(Bytes → KB/MB/GB)

        function formatFileSize(bytes) {

            if (bytes === 0) return '0 Bytes';

            const k = 1024;

            const units = ['Bytes', 'KB', 'MB', 'GB'];

            const i = Math.floor(Math.log(bytes) / Math.log(k));

            return `${(bytes / Math.pow(k, i)).toFixed(2)} ${units[i]}`;

        }

    </script>

</body>

</html>

3. 关键特性与限制

  • 优势:兼容性强(支持 Chrome 15+、Firefox 4+、Safari 6+),无需额外依赖,实现简单。

  • 限制

  1. 无法直接识别 “目录” 类型,需通过 typesize 间接判断;

  2. 仅能获取选中目录下的 “扁平化文件列表”,无法递归获取子目录结构;

  3. 无文件读写能力,仅能获取元数据。



三、方案二:基于 FileSystem Access API 实现(功能优先)

1. 方案概述

FileSystem Access API 是 W3C 正在标准化的现代 API(目前主要支持 Chromium 内核浏览器,如 Chrome 86+、Edge 86+),提供 “目录选择、递归遍历、文件读写、持久化权限” 等更强大的能力。通过 window.showDirectoryPicker() 直接请求用户授权,授权后可主动遍历目录结构,支持复杂的文件操作。


2. 完整使用示例

2.1 HTML 结构(含子目录遍历功能)

<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <title>FileSystem Access API 目录访问示例</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <style>

        .dir-tree-item { padding: 4px 0 4px 16px; border-left: 1px solid #eee; }

        .dir-header { display: flex; align-items: center; cursor: pointer; padding: 4px 0; }

        .dir-icon { margin-right: 8px; }

        .file-meta { color: #666; margin-left: 8px; }

    </style>

</head>

<body class="p-8 bg-gray-50">

    <div class="max-w-4xl mx-auto bg-white p-6 rounded-lg shadow">

        <h2 class="text-2xl font-bold mb-4">FileSystem Access API 目录遍历</h2>

        <!-- 触发目录选择按钮 -->

        <button id="openDirBtn" class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">

            打开并遍历目录

        </button>

        

        <!-- 目录树展示区 -->

        <div class="mt-4 border rounded-lg p-4 max-h-80 overflow-y-auto">

            <div id="dirTree" class="text-gray-500">

                请点击按钮选择目录

            </div>

        </div>

    </div>


    <script>

        // 2.2 JavaScript 逻辑实现(含递归遍历)

        const openDirBtn = document.getElementById('openDirBtn');

        const dirTree = document.getElementById('dirTree');


        openDirBtn.addEventListener('click', async () => {

            try {

                // 1. 检查浏览器兼容性

                if (!window.showDirectoryPicker) {

                    alert('您的浏览器不支持该功能,请使用 Chrome 或 Edge 浏览器');

                    return;

                }


                // 2. 请求用户选择目录(获取 DirectoryHandle 对象)

                const dirHandle = await window.showDirectoryPicker({

                    mode: 'read', // 权限模式:read(只读)/ readwrite(读写)

                    startIn: 'documents' // 默认打开目录(可选:documents、downloads 等)

                });


                // 3. 递归遍历目录结构并渲染

                dirTree.innerHTML = '<div class="text-center text-gray-500">正在读取目录...</div>';

                const treeHtml = await renderDirectoryTree(dirHandle, 0);

                dirTree.innerHTML = treeHtml;


            } catch (err) {

                // 捕获用户取消选择或权限拒绝错误

                if (err.name === 'AbortError') {

                    dirTree.innerHTML = '<div class="text-center text-gray-500">用户取消选择</div>';

                } else {

                    dirTree.innerHTML = `<div class="text-center text-red-500">错误:${err.message}</div>`;

                    console.error('目录访问失败:', err);

                }

            }

        });


        /**

         * 递归渲染目录树

         * @param {DirectoryHandle} handle - 目录/文件句柄

         * @param {number} depth - 目录深度(用于缩进)

         * @returns {string} 目录树 HTML

         */

        async function renderDirectoryTree(handle, depth) {

            const isDir = handle.kind === 'directory';

            const indent = 'margin-left: ' + (depth * 16) + 'px;'; // 按深度缩进

            let itemHtml = '';


            if (isDir) {

                // 处理目录:添加展开/折叠功能

                itemHtml += `

                    <div class="dir-header" style="${indent}" onclick="toggleDir(this)">

                        <span class="dir-icon text-yellow-500">📁</span>

                        <span class="font-medium">${handle.name}</span>

                        <span class="file-meta">(目录)</span>

                    </div>

                    <div class="dir-children" style="display: none;">

                `;


                // 遍历目录下的所有子项(递归)

                for await (const childHandle of handle.values()) {

                    itemHtml += await renderDirectoryTree(childHandle, depth + 1);

                }


                itemHtml += '</div>'; // 闭合 dir-children


            } else {

                // 处理文件:获取文件大小等元数据

                const file = await handle.getFile();

                const fileSize = formatFileSize(file.size);

                itemHtml += `

                    <div style="${indent} display: flex; align-items: center; padding: 4px 0;">

                        <span class="dir-icon text-gray-400">📄</span>

                        <span>${handle.name}</span>

                        <span class="file-meta">${fileSize}</span>

                    </div>

                `;

            }


            return itemHtml;

        }


        // 目录展开/折叠切换(全局函数,用于 HTML 内联调用)

        function toggleDir(el) {

            const children = el.nextElementSibling;

            children.style.display = children.style.display === 'none' ? 'block' : 'none';

            el.querySelector('.dir-icon').textContent = children.style.display === 'none' ? '📁' : '📂';

        }


        // 复用文件大小格式化函数(同方案一)

        function formatFileSize(bytes) {

            if (bytes === 0) return '0 Bytes';

            const k = 1024;

            const units = ['Bytes', 'KB', 'MB', 'GB'];

            const i = Math.floor(Math.log(bytes) / Math.log(k));

            return `${(bytes / Math.pow(k, i)).toFixed(2)} ${units[i]}`;

        }

    </script>

</body>

</html>

3. 关键特性与限制

  • 优势

  1. 直接识别 “目录 / 文件” 类型(通过 handle.kind);

  2. 支持递归遍历目录结构,可实现 “目录树” 交互;

  3. 提供文件读写能力(通过 fileHandle.createWritable());

  4. 可请求持久化权限(handle.requestPermission()),下次访问无需重新授权。

  • 限制:兼容性差,仅支持 Chromium 内核浏览器,Firefox 和 Safari 暂不支持。


四、两种方案对比分析

 


五、注意事项与最佳实践

  1. 安全合规:无论哪种方案,都必须通过 “用户主动操作” 触发授权(如点击按钮),禁止自动触发目录选择,否则浏览器会拦截操作。

  2. 错误处理:需捕获 “用户取消选择”(AbortError)和 “权限拒绝”(PermissionDeniedError)等错误,避免页面展示异常。

  3. 兼容性适配:可通过 “特性检测” 实现方案降级,例如:


if (window.showDirectoryPicker) {

    // 使用方案二(FileSystem Access API)

} else {

    // 使用方案一(File API)

}


  1. 性能优化:遍历大量文件时(如超过 1000 个文件),建议使用 “分页加载” 或 “虚拟滚动”,避免一次性渲染导致页面卡顿。

  2. 隐私保护:不建议存储用户本地文件路径等敏感信息,仅在前端临时处理文件数据,避免隐私泄露风险。


参考文章:原文链接


该文章在 2026/2/7 11:26:58 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved