前端JavaScript獲取本地文件目錄內(nèi)容的兩種實(shí)現(xiàn)方案
一、核心原理說明
由于瀏覽器的 “沙箱安全機(jī)制”,前端 JavaScript 無法直接訪問本地文件系統(tǒng),必須通過用戶主動授權(quán)(如選擇目錄操作)才能獲取文件目錄內(nèi)容。目前主流實(shí)現(xiàn)方案基于兩種 API:傳統(tǒng) File API(兼容性優(yōu)先)和現(xiàn)代 FileSystem Access API(功能優(yōu)先),以下將詳細(xì)介紹兩種方案的實(shí)現(xiàn)流程、代碼示例及適用場景。
二、方案一:基于 File API 實(shí)現(xiàn)(兼容性首選)
1. 方案概述
通過隱藏的 <input type="file"> 標(biāo)簽(配置 webkitdirectory 和 directory 屬性)觸發(fā)用戶選擇目錄操作,用戶選擇后通過 files 屬性獲取目錄下所有文件的元數(shù)據(jù)(如文件名、大小、相對路徑等)。該方案兼容幾乎所有現(xiàn)代瀏覽器(包括 Chrome、Firefox、Safari 等),但僅支持 “一次性獲取選中目錄內(nèi)容”,無法遞歸遍歷子目錄或修改文件。
2. 完整使用示例
2.1 HTML 結(jié)構(gòu)(含 UI 交互區(qū))
<!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; font-size: 14px; }
</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 目錄內(nèi)容獲取</h2>
<!-- 觸發(fā)按鈕(隱藏原生 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;">
<!-- 文件列表展示區(qū) -->
<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 邏輯實(shí)現(xiàn)
const dirInput = document.getElementById('dirInput');
const selectDirBtn = document.getElementById('selectDirBtn');
const fileList = document.getElementById('fileList');
// 1. 點(diǎn)擊按鈕觸發(fā)原生 input 選擇目錄
selectDirBtn.addEventListener('click', () => {
dirInput.click();
});
// 2. 監(jiān)聽目錄選擇變化,處理文件數(shù)據(jù)
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. 解析文件數(shù)據(jù)并渲染到頁面
let fileHtml = '';
Array.from(selectedFiles).forEach(file => {
// 判斷是否為目錄(通過 type 為空且 size 為 0 間接判斷)
const isDir = file.type === '' && file.size === 0;
// 獲取文件在目錄中的相對路徑(webkitRelativePath 為非標(biāo)準(zhǔn)屬性,但主流瀏覽器支持)
const relativePath = file.webkitRelativePath || file.name;
// 格式化文件大?。ㄝo助函數(shù))
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;
});
// 輔助函數(shù):格式化文件大小(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. 關(guān)鍵特性與限制
優(yōu)勢:兼容性強(qiáng)(支持 Chrome 15+、Firefox 4+、Safari 6+),無需額外依賴,實(shí)現(xiàn)簡單。
限制:
- 無法直接識別 “目錄” 類型,需通過
type和size間接判斷; - 僅能獲取選中目錄下的 “扁平化文件列表”,無法遞歸獲取子目錄結(jié)構(gòu);
- 無文件讀寫能力,僅能獲取元數(shù)據(jù)。
三、方案二:基于 FileSystem Access API 實(shí)現(xiàn)(功能優(yōu)先)
1. 方案概述
FileSystem Access API 是 W3C 正在標(biāo)準(zhǔn)化的現(xiàn)代 API(目前主要支持 Chromium 內(nèi)核瀏覽器,如 Chrome 86+、Edge 86+),提供 “目錄選擇、遞歸遍歷、文件讀寫、持久化權(quán)限” 等更強(qiáng)大的能力。通過 window.showDirectoryPicker() 直接請求用戶授權(quán),授權(quán)后可主動遍歷目錄結(jié)構(gòu),支持復(fù)雜的文件操作。
2. 完整使用示例
2.1 HTML 結(jié)構(gòu)(含子目錄遍歷功能)
<!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; font-size: 14px; 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>
<!-- 觸發(fā)目錄選擇按鈕 -->
<button id="openDirBtn" class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">
打開并遍歷目錄
</button>
<!-- 目錄樹展示區(qū) -->
<div class="mt-4 border rounded-lg p-4 max-h-80 overflow-y-auto">
<div id="dirTree" class="text-gray-500">
請點(diǎn)擊按鈕選擇目錄
</div>
</div>
</div>
<script>
// 2.2 JavaScript 邏輯實(shí)現(xiàn)(含遞歸遍歷)
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', // 權(quán)限模式:read(只讀)/ readwrite(讀寫)
startIn: 'documents' // 默認(rèn)打開目錄(可選:documents、downloads 等)
});
// 3. 遞歸遍歷目錄結(jié)構(gòu)并渲染
dirTree.innerHTML = '<div class="text-center text-gray-500">正在讀取目錄...</div>';
const treeHtml = await renderDirectoryTree(dirHandle, 0);
dirTree.innerHTML = treeHtml;
} catch (err) {
// 捕獲用戶取消選擇或權(quán)限拒絕錯誤
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 - 目錄深度(用于縮進(jìn))
* @returns {string} 目錄樹 HTML
*/
async function renderDirectoryTree(handle, depth) {
const isDir = handle.kind === 'directory';
const indent = 'margin-left: ' + (depth * 16) + 'px;'; // 按深度縮進(jìn)
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;">
`;
// 遍歷目錄下的所有子項(xiàng)(遞歸)
for await (const childHandle of handle.values()) {
itemHtml += await renderDirectoryTree(childHandle, depth + 1);
}
itemHtml += '</div>'; // 閉合 dir-children
} else {
// 處理文件:獲取文件大小等元數(shù)據(jù)
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;
}
// 目錄展開/折疊切換(全局函數(shù),用于 HTML 內(nèi)聯(lián)調(diào)用)
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' ? '??' : '??';
}
// 復(fù)用文件大小格式化函數(shù)(同方案一)
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. 關(guān)鍵特性與限制
優(yōu)勢:
- 直接識別 “目錄 / 文件” 類型(通過
handle.kind); - 支持遞歸遍歷目錄結(jié)構(gòu),可實(shí)現(xiàn) “目錄樹” 交互;
- 提供文件讀寫能力(通過
fileHandle.createWritable()); - 可請求持久化權(quán)限(
handle.requestPermission()),下次訪問無需重新授權(quán)。
限制:兼容性差,僅支持 Chromium 內(nèi)核瀏覽器,F(xiàn)irefox 和 Safari 暫不支持。
四、兩種方案對比分析
| 對比維度 | 方案一(File API) | 方案二(FileSystem Access API) |
|---|---|---|
| 瀏覽器兼容性 | 強(qiáng)(支持所有現(xiàn)代瀏覽器) | 弱(僅 Chromium 內(nèi)核瀏覽器) |
| 目錄識別能力 | 間接判斷(依賴 type 和 size) | 直接識別(handle.kind) |
| 目錄遍歷能力 | 僅扁平化列表,無遞歸支持 | 支持遞歸遍歷,可構(gòu)建目錄樹 |
| 文件操作能力 | 僅讀取元數(shù)據(jù),無讀寫能力 | 支持文件讀寫、刪除等完整操作 |
| 權(quán)限持久化 | 不支持(每次刷新需重新選擇) | 支持(可請求持久化權(quán)限) |
| 交互體驗(yàn) | 依賴隱藏 input,體驗(yàn)較基礎(chǔ) | 原生 API 調(diào)用,體驗(yàn)更流暢 |
| 適用場景 | 兼容性優(yōu)先的簡單目錄查看需求 | 現(xiàn)代瀏覽器下的復(fù)雜文件管理需求 |
五、注意事項(xiàng)與最佳實(shí)踐
- 安全合規(guī):無論哪種方案,都必須通過 “用戶主動操作” 觸發(fā)授權(quán)(如點(diǎn)擊按鈕),禁止自動觸發(fā)目錄選擇,否則瀏覽器會攔截操作。
- 錯誤處理:需捕獲 “用戶取消選擇”(AbortError)和 “權(quán)限拒絕”(PermissionDeniedError)等錯誤,避免頁面展示異常。
- 兼容性適配:可通過 “特性檢測” 實(shí)現(xiàn)方案降級,例如:
if (window.showDirectoryPicker) {
// 使用方案二(FileSystem Access API)
} else {
// 使用方案一(File API)
}
- 性能優(yōu)化:遍歷大量文件時(如超過 1000 個文件),建議使用 “分頁加載” 或 “虛擬滾動”,避免一次性渲染導(dǎo)致頁面卡頓。
- 隱私保護(hù):不建議存儲用戶本地文件路徑等敏感信息,僅在前端臨時處理文件數(shù)據(jù),避免隱私泄露風(fēng)險(xiǎn)。
以上就是前端JavaScript獲取本地文件目錄內(nèi)容的兩種實(shí)現(xiàn)方案的詳細(xì)內(nèi)容,更多關(guān)于前端JS獲取本地文件目錄內(nèi)容的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript 函數(shù)及作用域總結(jié)介紹
本文是對javascript在的函數(shù)及作用域進(jìn)行了詳細(xì)的總結(jié)介紹,需要的朋友可以過來參考下,希望對大家有所幫助2013-11-11
4種JavaScript實(shí)現(xiàn)簡單tab選項(xiàng)卡切換的方法
這篇文章主要介紹了4種JavaScript實(shí)現(xiàn)簡單tab選項(xiàng)卡切換的方法,感興趣的小伙伴們可以參考一下2016-01-01
ES6 系列之 Generator 的自動執(zhí)行的方法示例
這篇文章主要介紹了ES6 系列之 Generator 的自動執(zhí)行的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-10-10
詳解JavaScript中的強(qiáng)制類型轉(zhuǎn)換
這篇文章主要介紹了JavaScript中的強(qiáng)制類型轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04

