electron中拖动文件到桌面案例

效果图

main.js

javascript 复制代码
const path = require('node:path');
const { app, BrowserWindow, ipcMain, nativeImage } = require('electron');

const SAFE_FILES = ['file1.txt'];

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    title: 'File Drag Demo',
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false,
    },
  });

  win.loadFile('index.html');
}

app.whenReady().then(() => {
  ipcMain.on('get-draggable-files-sync', (event) => {
    event.returnValue = SAFE_FILES.map((name) => ({
      name,
      path: path.resolve(__dirname, name),
    }));
  });

  ipcMain.on('start-file-drag-sync', (event, filePath) => {
    const allowedPaths = new Set(SAFE_FILES.map((name) => path.resolve(__dirname, name)));
    const targetPath = path.resolve(filePath);
    event.returnValue = false;

    if (!allowedPaths.has(targetPath)) {
      return;
    }

    const iconPath = path.join(__dirname, 'drag-icon.png');
    const dragIcon = nativeImage.createFromPath(iconPath);

    if (dragIcon.isEmpty()) {
      console.error(`Failed to decode drag icon: ${iconPath}`);
      return;
    }

    try {
      event.sender.startDrag({
        file: targetPath,
        icon: dragIcon,
      });
      event.returnValue = true;
    } catch (error) {
      console.error('startDrag failed:', error);
    }
  });

  createWindow();

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

preload.js

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('fileDrag', {
  list() {
    return ipcRenderer.sendSync('get-draggable-files-sync');
  },
  start(filePath) {
    return ipcRenderer.sendSync('start-file-drag-sync', filePath);
  },
});

renderer.js

javascript 复制代码
const fileList = document.querySelector('#fileList');
const files = window.fileDrag.list();

for (const fileEntry of files) {
  const item = document.createElement('div');
  item.className = 'file-item';
  item.draggable = true;
  item.textContent = fileEntry.name;

  item.addEventListener('dragstart', (event) => {
    const ok = window.fileDrag.start(fileEntry.path);
    if (!ok) {
      event.preventDefault();
      console.error(`Drag rejected for ${fileEntry.name}`);
    }
  });

  fileList.append(item);
}

index.html

html 复制代码
<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Electron 文件拖拽 Demo</title>
    <link rel="stylesheet" href="./styles.css" />
  </head>
  <body>
    <main class="app">
      <h1>拖文件到桌面</h1>
      <p>按住下面任意文件,直接拖到桌面或文件夹里。</p>

      <section class="file-list" id="fileList"></section>
    </main>

    <script src="./renderer.js"></script>
  </body>
</html>

style.css

css 复制代码
:root {
  color-scheme: light dark;
  font-family: Arial, "Microsoft YaHei", sans-serif;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
}

.app {
  max-width: 420px;
  margin: 48px auto;
  padding: 0 24px;
}

h1 {
  margin: 0 0 8px;
  font-size: 28px;
}

p {
  margin: 0 0 24px;
  color: #666;
}

.file-list {
  display: grid;
  gap: 12px;
}

.file-item {
  padding: 16px 18px;
  border: 1px solid #d6d6d6;
  border-radius: 12px;
  background: #f7f7f7;
  cursor: grab;
  user-select: none;
}

.file-item:active {
  cursor: grabbing;
}

@media (prefers-color-scheme: dark) {
  body {
    background: #202124;
    color: white;
  }

  p {
    color: #bdbdbd;
  }

  .file-item {
    border-color: #555;
    background: #2b2c2f;
  }
}

@media (prefers-color-scheme: light) {
  body {
    background: #fff;
    color: black;
  }
}
相关推荐
阿成学长_Cain2 小时前
Linux telinit 命令详解:运行级别切换|关机重启|系统维护一站式掌握
linux·运维·前端·网络
Patrick_Wilson2 小时前
最佳实践是有保质期的:从一次 CDN external 白屏事故说起
前端·性能优化·前端工程化
YHHLAI2 小时前
[特殊字符] Agent 智能体开发实战 · 第一课:Tool Use —— 让大模型自动干活
前端·人工智能
nuIl3 小时前
我把 5 个编码 Agent 塞进了一个 npm 包
前端·agent·claude
L-影3 小时前
FastAPI 静态文件:Web 页面的“固定展柜”与“加速引擎”
前端·fastapi
陪我去看海3 小时前
受够了 AI 写完代码留下一堆端口?我做了一个端口管理App!
前端·macos·vibecoding
Xuepoo3 小时前
我抛弃了 DOM,但保留了无障碍访问
前端
李明卫杭州3 小时前
Vue2 vs Vue3 的 h 函数:渲染函数完整迁移指南
前端
月光刺眼3 小时前
用LangChain 打造第一个 Agent:LLM 大脑与 Tool 手脚的完整闭环
javascript·人工智能·全栈
星栈3 小时前
LiveView 的认证系统:从登录到权限,我一开始以为有 `current_user` 就算完事了
前端·前端框架·elixir