本文目录导读:

网页端实现(HTML5 + JavaScript)
基础拖拽上传
<!DOCTYPE html>
<html>
<head>
<style>
#dropZone {
width: 400px;
height: 200px;
border: 2px dashed #ccc;
border-radius: 10px;
text-align: center;
line-height: 200px;
font-size: 18px;
color: #666;
transition: all 0.3s;
}
#dropZone.dragover {
border-color: #4CAF50;
background: #e8f5e9;
color: #4CAF50;
}
</style>
</head>
<body>
<div id="dropZone">拖拽文件到这里</div>
<div id="fileList"></div>
<script>
const dropZone = document.getElementById('dropZone');
const fileList = document.getElementById('fileList');
// 防止浏览器默认打开文件
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, (e) => {
e.preventDefault();
e.stopPropagation();
});
});
// 拖拽高亮效果
['dragenter', 'dragover'].forEach(eventName => {
dropZone.addEventListener(eventName, () => {
dropZone.classList.add('dragover');
});
});
['dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, () => {
dropZone.classList.remove('dragover');
});
});
// 处理文件拖放
dropZone.addEventListener('drop', (e) => {
const files = e.dataTransfer.files;
handleFiles(files);
});
function handleFiles(files) {
for (const file of files) {
// 显示文件信息
const div = document.createElement('div');
div.textContent = `${file.name} - ${file.size} bytes - ${file.type}`;
fileList.appendChild(div);
// 这里可以上传文件或做其他处理
console.log('文件信息:', {
name: file.name,
size: file.size,
type: file.type,
lastModified: file.lastModified
});
}
}
</script>
</body>
</html>
带上传功能的完整示例
<!DOCTYPE html>
<html>
<head>
<style>
#dropZone {
width: 500px;
height: 300px;
border: 2px dashed #ccc;
border-radius: 10px;
margin: 20px auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
#dropZone.dragover {
border-color: #2196F3;
background: #E3F2FD;
}
#dropZone .icon {
font-size: 48px;
margin-bottom: 10px;
}
#dropZone .text {
font-size: 16px;
color: #666;
}
.upload-progress {
width: 80%;
margin: 20px auto;
}
.progress-bar {
background: #f0f0f0;
border-radius: 5px;
padding: 3px;
}
.progress-bar-fill {
background: #4CAF50;
border-radius: 5px;
height: 20px;
width: 0%;
transition: width 0.3s;
}
</style>
</head>
<body>
<div id="dropZone">
<div class="icon">📁</div>
<div class="text">拖拽文件到此处上传</div>
</div>
<div id="uploads"></div>
<script>
const dropZone = document.getElementById('dropZone');
// 拖拽事件处理
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, preventDefaults);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
// 高亮效果
['dragenter', 'dragover'].forEach(eventName => {
dropZone.addEventListener(eventName, highlight);
});
['dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, unhighlight);
});
function highlight() {
dropZone.classList.add('dragover');
}
function unhighlight() {
dropZone.classList.remove('dragover');
}
// 处理文件拖放
dropZone.addEventListener('drop', handleDrop);
function handleDrop(e) {
const data = e.dataTransfer;
const files = data.files;
// 检查是否有文件
if (files.length) {
// 检查文件类型(可选)
const validFiles = [...files].filter(file => {
// 这里可以添加文件类型限制
// 只接受图片
// return file.type.startsWith('image/');
return true;
});
validFiles.forEach(uploadFile);
}
}
// 模拟文件上传
function uploadFile(file) {
const uploads = document.getElementById('uploads');
const uploadItem = document.createElement('div');
uploadItem.className = 'upload-item';
uploadItem.innerHTML = `
<div>${file.name} (${formatFileSize(file.size)})</div>
<div class="upload-progress">
<div class="progress-bar">
<div class="progress-bar-fill"></div>
</div>
</div>
<div class="status">上传中...</div>
`;
uploads.appendChild(uploadItem);
// 模拟上传进度
let progress = 0;
const fillBar = uploadItem.querySelector('.progress-bar-fill');
const status = uploadItem.querySelector('.status');
const interval = setInterval(() => {
progress += 10;
fillBar.style.width = progress + '%';
if (progress >= 100) {
clearInterval(interval);
status.textContent = '✅ 上传完成';
fillBar.style.background = '#4CAF50';
}
}, 100);
// 实际项目中这里应该使用 FormData 和 fetch/XMLHttpRequest
// const formData = new FormData();
// formData.append('file', file);
// fetch('/upload', { method: 'POST', body: formData });
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
</script>
</body>
</html>
Electron 桌面应用(Node.js + HTML)
// main.js (Electron 主进程)
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const fs = require('fs');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
preload: path.join(__dirname, 'preload.js')
}
});
win.loadFile('index.html');
}
app.whenReady().then(createWindow);
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<style>
#dropZone {
width: 100%;
height: 400px;
border: 2px dashed #4CAF50;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 20px;
color: #4CAF50;
}
#dropZone.dragover {
background: #E8F5E9;
}
.file-list {
margin-top: 20px;
padding: 15px;
}
.file-item {
background: #f5f5f5;
margin: 5px 0;
padding: 10px;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.file-info {
color: #666;
}
.save-btn {
background: #4CAF50;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="dropZone">
🖱️ 拖拽文件到这里
</div>
<div id="fileList" class="file-list"></div>
<script>
const dropZone = document.getElementById('dropZone');
const fileList = document.getElementById('fileList');
const droppedFiles = [];
// 拖拽事件处理
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, (e) => {
e.preventDefault();
e.stopPropagation();
});
});
// 高亮效果
['dragenter', 'dragover'].forEach(eventName => {
dropZone.addEventListener(eventName, () => {
dropZone.classList.add('dragover');
});
});
['dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, () => {
dropZone.classList.remove('dragover');
});
});
// 处理文件拖放
dropZone.addEventListener('drop', async (e) => {
const items = e.dataTransfer.items;
for (const item of items) {
if (item.kind === 'file') {
// 获取文件对象
const file = item.getAsFile();
droppedFiles.push(file);
const div = document.createElement('div');
div.className = 'file-item';
div.innerHTML = `
<div>
<strong>${file.name}</strong>
<div class="file-info">
大小: ${(file.size / 1024).toFixed(2)} KB |
类型: ${file.type || '未知'} |
修改时间: ${new Date(file.lastModified).toLocaleDateString()}
</div>
</div>
<button class="save-btn" onclick="saveFile('${file.name}')">保存</button>
`;
fileList.appendChild(div);
}
}
});
// 保存文件到指定位置
async function saveFile(fileName) {
const file = droppedFiles.find(f => f.name === fileName);
if (file) {
try {
// 使用 Electron 的 dialog 选择保存位置
const result = await window.electronAPI.saveFile(fileName);
if (result.success) {
// 将文件内容写入
const reader = new FileReader();
reader.onload = async (e) => {
await window.electronAPI.writeFile(result.path, e.target.result);
alert('文件保存成功:' + result.path);
};
reader.readAsDataURL(file);
}
} catch (error) {
console.error('保存失败:', error);
alert('保存失败:' + error.message);
}
}
}
</script>
</body>
</html>
// preload.js (Electron 预加载脚本)
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
// 获取文件信息
getFilePath: (file) => ipcRenderer.invoke('get-file-path', file),
// 保存文件
saveFile: (fileName) => ipcRenderer.invoke('save-file', fileName),
// 写入文件内容
writeFile: (path, data) => ipcRenderer.invoke('write-file', { path, data })
});
Python 桌面应用(Tkinter / PyQt)
Tkinter 版本
import tkinter as tk
from tkinter import ttk
from tkinterdnd2 import DND_FILES, TkinterDnD
class DragDropApp(TkinterDnD.Tk):
def __init__(self):
super().__init__()
self.title("文件拖拽处理")
self.geometry("500x400")
# 创建拖拽区域
self.drop_zone = tk.Label(
self,
text="📁 拖拽文件到这里",
bg="#f0f0f0",
relief="solid",
borderwidth=2,
font=("Arial", 16)
)
self.drop_zone.pack(pady=20, padx=20, fill="both", expand=True)
# 注册拖拽事件
self.drop_zone.drop_target_register(DND_FILES)
self.drop_zone.dnd_bind('<<Drop>>', self.on_drop)
self.drop_zone.dnd_bind('<<DragEnter>>', self.on_drag_enter)
self.drop_zone.dnd_bind('<<DragLeave>>', self.on_drag_leave)
# 文件列表
self.file_list = tk.Listbox(self, height=10)
self.file_list.pack(pady=10, padx=20, fill="both", expand=True)
# 处理按钮
btn_frame = tk.Frame(self)
btn_frame.pack(pady=10)
tk.Button(btn_frame, text="处理文件", command=self.process_files).pack(side="left", padx=5)
tk.Button(btn_frame, text="清空列表", command=self.clear_list).pack(side="left", padx=5)
self.files = []
def on_drag_enter(self, event):
self.drop_zone.config(bg="#e8f5e9", relief="solid")
def on_drag_leave(self, event):
self.drop_zone.config(bg="#f0f0f0")
def on_drop(self, event):
self.drop_zone.config(bg="#f0f0f0")
# 解析拖拽的文件路径
files = self.tk.splitlist(event.data)
for file in files:
self.files.append(file)
self.file_list.insert("end", file)
def process_files(self):
if not self.files:
print("没有文件需要处理")
return
# 这里添加具体的文件处理逻辑
for file in self.files:
print(f"正在处理: {file}")
# 示例:获取文件大小
import os
size = os.path.getsize(file)
print(f" 文件大小: {size} bytes")
print("文件处理完成!")
def clear_list(self):
self.files.clear()
self.file_list.delete(0, "end")
if __name__ == "__main__":
app = DragDropApp()
app.mainloop()
PyQt5 版本
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget, QPushButton, QListWidget, QMessageBox
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QDragEnterEvent, QDropEvent
class DragDropWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("文件拖拽处理")
self.setGeometry(100, 100, 500, 400)
# 主部件设置
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout()
central_widget.setLayout(layout)
# 拖拽区域
self.drop_label = QLabel(
"📁 拖拽文件到这里\n\n支持多文件选择",
self
)
self.drop_label.setAlignment(Qt.AlignCenter)
self.drop_label.setStyleSheet("""
QLabel {
border: 2px dashed #4CAF50;
border-radius: 10px;
padding: 40px;
font-size: 16px;
background: #f0f0f0;
}
""")
layout.addWidget(self.drop_label)
# 文件列表
self.file_list = QListWidget()
layout.addWidget(self.file_list)
# 按钮
process_btn = QPushButton("处理文件", self)
process_btn.clicked.connect(self.process_files)
layout.addWidget(process_btn)
# 启用拖拽
self.setAcceptDrops(True)
self.files = []
def dragEnterEvent(self, event: QDragEnterEvent):
if event.mimeData().hasUrls():
event.accept()
self.drop_label.setStyleSheet("""
QLabel {
border: 2px dashed #2196F3;
border-radius: 10px;
padding: 40px;
font-size: 16px;
background: #E3F2FD;
}
""")
else:
event.ignore()
def dragLeaveEvent(self, event):
self.drop_label.setStyleSheet("""
QLabel {
border: 2px dashed #4CAF50;
border-radius: 10px;
padding: 40px;
font-size: 16px;
background: #f0f0f0;
}
""")
def dropEvent(self, event: QDropEvent):
self.drop_label.setStyleSheet("""
QLabel {
border: 2px dashed #4CAF50;
border-radius: 10px;
padding: 40px;
font-size: 16px;
background: #f0f0f0;
}
""")
files = [url.toLocalFile() for url in event.mimeData().urls()]
for file in files:
if file: # 确保不是空路径
self.files.append(file)
self.file_list.addItem(file)
def process_files(self):
if not self.files:
QMessageBox.warning(self, "提示", "请先拖入文件")
return
# 这里添加你的文件处理逻辑
for file in self.files:
import os
if os.path.isfile(file):
size = os.path.getsize(file)
print(f"处理文件: {file} (大小: {size} bytes)")
QMessageBox.information(self, "成功", "文件处理完成!")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = DragDropWindow()
window.show()
sys.exit(app.exec_())
网页端
- 使用
dragenter、dragover、drop事件 - 必须调用
e.preventDefault()阻止默认行为 - 通过
e.dataTransfer.files获取文件 - 支持多文件拖拽
Electron
- 结合 HTML5 和 Node.js
- 可通过 IPC 与主进程通信
- 可访问文件系统进行保存等操作
Python 桌面
- Tkinter 需要安装
tkinterdnd2 - PyQt5 原生支持拖拽
- 事件处理类似 Web
选择哪种方案取决于你的具体需求(Web 应用、桌面应用、跨平台等)。