在浏览器环境中,由于安全限制,无法直接获取目录的完整路径,但可以通过选择目录后提取其路径信息(不含文件名)。
webkitRelativePath:
- 纯目录路径提取逻辑:
- 通过
webkitRelativePath
处理webkit内核浏览器(Chrome/Edge/Safari):当选择目录时,该属性会返回 "目录名/文件名" 格式,通过截取第一个/
之前的部分获取纯目录名 - 对支持完整路径的环境(如Electron),通过
lastIndexOf('/')
截取目录部分 - 确保结果中不包含任何文件名
- 用户体验优化:
- 添加状态显示已选目录
- 禁用了文件多选(
multiple={false}
),更符合目录选择的语义
- 浏览器兼容性:
- 主流现代浏览器(Chrome/Edge/Firefox 78+/Safari 11.1+)均支持
- 注意:在纯网页环境中,浏览器出于安全考虑不会返回完整的本地文件系统路径,只会返回目录名称或相对路径;如果需要完整路径,建议使用Electron等桌面应用框架 使用时,只需将选中的
directoryPath
用于后续业务逻辑即可,该路径已确保不包含任何文件名。
ts
import React, { useRef, useState } from 'react';
const DirectoryOnlyPicker: React.FC = () => {
const directoryInputRef = useRef<HTMLInputElement>(null);
const [selectedDir, setSelectedDir] = useState<string | null>(null);
// 处理目录选择,提取纯目录路径
const handleDirectorySelect = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = event.target.files;
if (!files || files.length === 0) return;
// 获取目录路径(通过第一个文件的webkitRelativePath推导)
const firstFile = files[0];
let directoryPath = '';
if (firstFile.webkitRelativePath) {
// webkit浏览器(Chrome/Edge/Safari):通过相对路径提取目录
const relativePath = firstFile.webkitRelativePath;
directoryPath = relativePath.substring(0, relativePath.indexOf('/'));
} else if (firstFile.path) {
// 部分环境(如Electron)支持直接获取完整路径
const fullPath = firstFile.path;
directoryPath = fullPath.substring(0, fullPath.lastIndexOf('/') + 1);
} else {
// fallback:使用目录名称
directoryPath = firstFile.name;
}
setSelectedDir(directoryPath);
console.log('选中的目录:', directoryPath);
};
const openDirectoryPicker = () => {
if (directoryInputRef.current) {
directoryInputRef.current.value = '';
directoryInputRef.current.click();
}
};
return (
<div style={{ padding: 20 }}>
<button
onClick={openDirectoryPicker}
style={{
padding: '8px 16px',
backgroundColor: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px'
}}
>
选择目录
</button>
{selectedDir && (
<div style={{ marginTop: 10, color: '#333' }}>
已选目录: <span style={{ fontWeight: 'bold' }}>{selectedDir}</span>
</div>
)}
{/* 隐藏的目录选择输入框 */}
<input
ref={directoryInputRef}
type="file"
webkitdirectory="true" // 核心属性:允许选择目录
directory="true"
onChange={handleDirectorySelect}
style={{ display: 'none' }}
multiple={false} // 不需要多选文件
/>
</div>
);
};
export default DirectoryOnlyPicker;
有坑:用electron打包后,我的电脑能获取到,但是我同事的(win11)获取不到D盘的路径,麻了,好在electron能救命
electron中实现:
就是在渲染进程中,触发点击事件后,发送对应的消息到主进程中,然后打开弹窗获取目录这个事教给主进程来做。主进程中实现:
ts
// main.js(Electron 主进程文件)
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const path = require('path'); let mainWindow;
// 创建窗口函数
function createWindow() {
mainWindow = new BrowserWindow({
width: 1000, height: 800,
webPreferences: {
// 关键配置:启用上下文隔离(安全默认),指定预加载脚本
contextIsolation: true,
// 预加载脚本路径(用于暴露 IPC 通信接口,避免直接暴露 Node 模块)
preload: path.join(__dirname, 'preload.js'),
// 禁用 nodeIntegration(安全默认,避免渲染进程直接访问 Node)
nodeIntegration: false
}
});
// 加载前端页面(根据你的项目路径调整,如 React 打包后的 index.html 或开发环境地址)
mainWindow.loadURL('http://localhost:3000'); // 开发环境
// mainWindow.loadFile(path.join(__dirname, 'build/index.html')); // 生产环境(打包后)
}
// 监听渲染进程的"选择目录"请求
ipcMain.handle('open-directory-dialog', async () => {
try {
// 调用 Electron 原生对话框选择目录
const result = await dialog.showOpenDialog(mainWindow, {
title: '选择目录',
properties: ['openDirectory', 'showHiddenFiles', 'createDirectory'],
// 支持创建、显示隐藏文件
defaultPath: 'D:\\' // 默认打开 D 盘(解决原问题:直接定位到非系统盘)
});
// 处理结果:若用户未取消,返回选中的目录路径;否则返回 null
if (!result.canceled && result.filePaths.length > 0) {
return result.filePaths[0];
// 返回第一个选中的目录(目录选择模式下仅一个)
} return null;
} catch (error) {
console.error('主进程目录选择失败:', error);
return null;
}
});
// 应用启动事件 app.whenReady().then(createWindow);
// 关闭所有窗口时退出应用(Windows/Linux)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
// Mac 平台: dock 图标点击时重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});