在前端开发中,文件上传是一个常见需求。随着浏览器 API 的不断进化,现在我们可以实现更灵活的文件交互方式。本文将总结出一个支持文件选择、文件夹选择、拖拽上传、粘贴上传的完整示例,涵盖现代文件操作的主流场景。
一、功能特性
1. 文件/文件夹选择方式
传统文件选择:通过按钮触发input[type=”file”]和multiple,支持多选文件
文件夹选择:使用webkitdirectory属性选择整个文件夹(需 Chrome 等 webkit 内核浏览器)
2. 拖拽上传
可拖拽文件 / 文件夹到指定区域
拖拽状态可视化(背景色变化)
支持跨窗口拖拽(如从文件资源管理器拖入浏览器)
3. 粘贴上传
支持Ctrl+V粘贴系统剪贴板中的文件(需浏览器支持 Clipboard API)
可粘贴单个文件或整个文件夹
4. 文件管理
实时显示已选文件列表
支持文件大小格式化(自动转换为 B/KB/MB 等单位)
点击文件条目直接下载
控制台输出文件数组
二、效果演示
三、完整代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件选择/拖拽/粘贴示例</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.area {
width: 100%;
height: 200px;
border: 2px dashed #ccc;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f9f9f9;
transition: background-color 0.3s;
cursor: pointer;
}
.area:hover {
background-color: #f0f0f0;
}
.area.dragover {
background-color: #e0e0e0;
border-color: #007BFF;
}
.file-list {
margin-top: 20px;
}
.file-item {
padding: 8px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
cursor: pointer;
}
.file-item:hover {
background-color: #f8f9fa;
}
.file-item:last-child {
border-bottom: none;
}
.file-name {
max-width: 600px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.file-size {
color: #666;
font-size: 0.9em;
margin-left: 10px;
}
.button-group {
margin-bottom: 15px;
}
button {
padding: 8px 15px;
margin-right: 10px;
border: none;
border-radius: 4px;
background-color: #007BFF;
color: white;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<h3>文件选择/拖拽/粘贴示例</h3>
<div class="button-group">
<button id="selectFilesBtn">选择文件</button>
<button id="selectFolderBtn">选择文件夹</button>
</div>
<input type="file" name="fileInput" id="fileInput" multiple style="display: none;">
<input type="file" name="folderInput" id="folderInput" webkitdirectory directory multiple style="display: none;">
<div class="area">拖放文件或文件夹到这里(支持使用ctrl+v粘贴所复制的文件)</div>
<div class="file-list"></div>
<script>
const doms = {
fileInput: document.getElementById('fileInput'),
folderInput: document.getElementById('folderInput'),
selectFilesBtn: document.getElementById('selectFilesBtn'),
selectFolderBtn: document.getElementById('selectFolderBtn'),
area: document.querySelector('.area'),
fileList: document.querySelector('.file-list')
}
// 存储当前文件列表
let currentFiles = [];
// 显示文件列表
function displayFiles() {
console.log('获取到的文件:', currentFiles);
doms.fileList.innerHTML = '';
currentFiles.forEach((file, index) => {
const item = document.createElement('div');
item.className = 'file-item';
item.innerHTML = `
<span class="file-name">${file.name}</span>
<span class="file-size">${formatFileSize(file.size)}</span>
`;
item.setAttribute('data-index', index);
item.addEventListener('click', handleFileClick);
doms.fileList.appendChild(item);
});
}
// 处理文件点击事件
function handleFileClick(e) {
const index = parseInt(e.currentTarget.getAttribute('data-index'));
const file = currentFiles[index];
if (file) {
const url = URL.createObjectURL(file);
const a = document.createElement('a');
a.href = url;
a.download = file.name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
// 释放URL对象以避免内存泄漏
setTimeout(() => {
URL.revokeObjectURL(url);
}, 100);
}
}
// 格式化文件大小
function formatFileSize(bytes) {
if (bytes < 1024) {
return parseFloat(bytes).toFixed(2) + "B";
} else if (bytes < 1024 * 1024) {
return (bytes / 1024).toFixed(2) + "KB";
} else if (bytes < 1024 * 1024 * 1024) {
return (bytes / (1024 * 1024)).toFixed(2) + "MB";
} else if (bytes < 1024 * 1024 * 1024 * 1024) {
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + "GB";
} else if (bytes < 1024 * 1024 * 1024 * 1024 * 1024) {
return (bytes / (1024 * 1024 * 1024 * 1024)).toFixed(2) + "TB";
} else {
return (bytes / (1024 * 1024 * 1024 * 1024 * 1024)).toFixed(2) + "PB";
}
}
// 递归读取文件夹并扁平化文件列表
function readFileList(entry) {
return new Promise((resolve, reject) => {
if (entry.isFile) {
entry.file(file => resolve(file));
} else if (entry.isDirectory) {
const reader = entry.createReader();
reader.readEntries(entries => {
const promises = entries.map(childEntry => readFileList(childEntry));
Promise.all(promises).then(files => resolve(files.flat()));
});
} else {
resolve([]);
}
});
}
// 处理文件入口
async function processEntries(items) {
const promises = items.map(item => {
// 处理拖拽/粘贴的文件系统条目
const entry = item.webkitGetAsEntry ? item.webkitGetAsEntry() : null;
if (entry) return readFileList(entry);
return [];
});
currentFiles = (await Promise.all(promises)).flat();
displayFiles();
}
// 文件选择按钮点击事件
doms.selectFilesBtn.addEventListener('click', () => {
doms.fileInput.click();
});
// 文件夹选择按钮点击事件
doms.selectFolderBtn.addEventListener('click', () => {
doms.folderInput.click();
});
// 文件选择事件
doms.fileInput.addEventListener('change', (e) => {
currentFiles = Array.from(e.target.files);
displayFiles();
});
// 文件夹选择事件
doms.folderInput.addEventListener('change', async (e) => {
currentFiles = Array.from(e.target.files);
displayFiles();
});
// 拖拽事件
doms.area.addEventListener('dragenter', (e) => {
e.preventDefault();
doms.area.classList.add('dragover');
});
doms.area.addEventListener('dragover', (e) => {
e.preventDefault();
doms.area.classList.add('dragover');
});
doms.area.addEventListener('dragleave', (e) => {
e.preventDefault();
doms.area.classList.remove('dragover');
});
doms.area.addEventListener('drop', async (e) => {
e.preventDefault();
doms.area.classList.remove('dragover');
if (e.dataTransfer.items) {
processEntries(Array.from(e.dataTransfer.items));
}
});
// 粘贴事件
document.addEventListener('paste', async (e) => {
if (e.clipboardData.items) {
processEntries(Array.from(e.clipboardData.items));
}
});
</script>
</body>
</html>
四、总结
| 方式 | 触发方式 | 技术实现 | 兼容性 |
|---|---|---|---|
| 文件选择 | 点击 “选择文件” 按钮 | input[type=”file”] + multiple属性 | 基础功能 |
| 文件夹选择 | 点击 “选择文件夹” 按钮 | webkitdirectory属性 | webkit引擎 |
| 拖拽上传 | 拖放文件到虚线区域 | 拖拽事件(drop)+ 递归解析 | 标准 File API 支持 |
| 粘贴上传 | 复制文件后按Ctrl+V | 粘贴事件(paste) + 剪贴板数据读取 + 递归解析 | 需浏览器支持 Clipboard API |







文章有(8)条网友点评
cenforce 200 tablet
cenforce 200 tablet
semaglutid tabletten handelsname
semaglutid tabletten handelsname
sildenafil benefits
sildenafil benefits
can doxycycline used for cold
can doxycycline used for cold
flagyl treatment dose for bv
flagyl treatment dose for bv
sildenafil citrate 100mg online
sildenafil citrate 100mg online
best minoxidil pills
best minoxidil pills
saxenda or manjaro
saxenda or manjaro