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;
  }
}
相关推荐
范同学~几秒前
固定表头+第一列+最后一列
java·服务器·前端
zttbee28 分钟前
npm install卡顿
前端·npm·node.js
Amos_Web32 分钟前
Rspack 源码解析(二十七):AsyncWasmParserAndGenerator 如何把二进制变成异步模块
前端·rust
西红柿炖牛腩35441 分钟前
机房电脑不能联网,怎么离线把课件里的 3D 模型压小?Draco / Meshopt 参数清单
前端·javascript·3d·性能优化·大屏端
爱喝水的小周43 分钟前
实验十三 Vue 过渡与动画—电商后台消息中心实现
前端·javascript·vue.js
hunterandroid1 小时前
Navigation Component 工程落地:从单 Activity 架构到深度链接配置
android·前端
无责任此方_修行中1 小时前
如何不用括号调用 JS 的函数?
前端·javascript·node.js
kyriewen1 小时前
我数了 647 条大厂前端 JD:AI 不再只是加分项
前端·程序员·ai编程
无责任此方_修行中1 小时前
Deno 的八年:一场没逃出 Node 引力的运行时起义
前端·javascript·node.js
java1234_小锋1 小时前
【技术专题】HTML5 网页技术 - HTML5 语义化标签
前端·html·html5