Electron开发-文件拖拽选择

前言

Electron + Vue 项目中实现 文件拖拽添加功能,核心思路与纯 HTML 类似,但需要结合 Vue 的响应式机制和组件结构,并遵循 Electron 的安全最佳实践(如使用预加载脚本、contextIsolation: true 等)。

下面以 Vue 3 + Electron(常见于 electron-vitevue-cli-plugin-electron-builder 项目)为例,展示完整实现。

目标

用户将文件拖到页面某个区域,获取文件路径列表,并在 Vue 组件中显示或处理。

Vue 组件中实现拖拽区域

模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div
class="drop_area"
@dragover.prevent.stop="handleDragOver"
@dragenter.prevent.stop="handleDragEnter"
@dragleave.prevent.stop="handleDragLeave"
@drop.prevent.stop="handleDrop"
:class="{ dragging: isDragging }"
>
<div class="drop_tip">{{ isDragging ? '松开以添加文件' : '拖拽文件到这里' }}</div>

<div class="img_list_outer" v-if="filePaths.length">
<div v-for="(path, index) in filePaths" :key="index" class="img_item">
<img :src="path" alt="">
</div>
</div>
</div>

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const isDragging = ref(false);
const filePaths: Ref<string[]> = ref<string[]>([]);

const handleDragOver = () => {
isDragging.value = true;
};

const handleDragEnter = () => {
isDragging.value = true;
};

const handleDragLeave = () => {
isDragging.value = false;
};

const handleDrop = (e: any) => {
isDragging.value = false;
const files = e.dataTransfer.files;
if (files && files.length > 0) {
// Electron 特有:File 对象有 .path 属性
const paths: string[] = Array.from(files).map((file: any) => file.path);
let tempPath = filePaths.value
const pathList = paths
.filter((path: string) => path.endsWith(".png") || path.endsWith(".jpg") || path.endsWith(".jpeg") || path.endsWith(".bmp"))
.filter((path: string) => tempPath.indexOf(path) < 0);
tempPath.push(...pathList)
filePaths.value = tempPath;
}
};

样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<style scoped>
.drop_area {
position: relative;
pointer-events: auto;
border: 2px dashed #ccc;
border-radius: 8px;
flex: auto;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: border-color 0.2s;
user-select: none;
}

.drop_area.dragging {
border-color: #007bff;
background-color: #f0f8ff;
}

.drop_area .drop_tip {
position: relative;
color: #999;
font-size: 14px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
}

.drop_area .img_list_outer {
width: 100%;
height: 0;
flex: auto;
display: flex;
flex-wrap: wrap;
overflow-y: auto;
}

.drop_area .img_item {
width: 20%;
height: 200px;
flex: none;
display: flex;
align-items: center;
justify-content: center;
}

.drop_area .img_item img {
width: 90%;
height: 90%;
object-fit: contain;
}
</style>

在 App.vue 中使用

1
2
3
4
5
6
7
8
9
10
<!-- src/App.vue -->
<template>
<div id="app">
<FileDropZone />
</div>
</template>

<script setup>
import FileDropZone from './components/FileDropZone.vue';
</script>

主进程配置(可选)

如果需要主进程处理文件,才需要进行IPC交互。

窗口设置

main.js / main.ts

确保 BrowserWindow 启用了预加载脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// main.js
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');

function createWindow() {
const win = new BrowserWindow({
width: 1000,
height: 700,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // 必须为 true(安全)
}
});

win.loadFile(path.join(__dirname, '../dist/index.html')); // 根据你的构建输出调整
}

app.whenReady().then(createWindow);

// 可选:主进程监听拖入的文件(如果需要在主进程处理)
ipcMain.on('files-dropped', (event, paths) => {
console.log('主进程收到拖拽文件:', paths);
});

预加载脚本

暴露安全的 API 给渲染进程:

1
2
3
4
5
6
7
// preload.js
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
sendFiles: (paths) => ipcRenderer.send('files-dropped', paths),
// 如果需要主进程返回数据,也可以加 invoke
});

💡 这样 Vue 组件就能通过 window.electronAPI.sendFiles(...) 调用。

构建配置注意事项

如果你使用的是:

  • Vite + Electron(如 electron-vite):确保 preload.js 被正确复制到输出目录。
  • Vue CLI + electron-builder:在 vue.config.js 中配置 preload:

设置预加载JS

1
2
3
4
5
6
7
8
9
// vue.config.js
module.exports = {
pluginOptions: {
electronBuilder: {
preload: 'src/preload.js',
// ...
}
}
}

常见问题排查

问题1

拖拽文件一直是禁用状态,不触发事件。

原因:

程序不能用管理员身份运行。

如果是在开发过程中,开发工具(例如:IDEA)也不要用管理员身份运行。

总结

Electron + Vue 中支持文件拖拽添加的关键步骤:

  1. 主进程配置 preload 脚本;
  2. 预加载脚本通过 contextBridge 暴露安全 API;
  3. Vue 组件监听 @drop 等事件,从 dataTransfer.files 获取文件;
  4. 利用 Electron 特有的 File.path 获取真实路径;
  5. 通过 IPC 与主进程通信(如需)。

这样既安全又符合现代 Electron 开发规范。