Electron 项目来实现文件下载和上传功能(AI)

首先,我们需要初始化项目并安装必要的依赖:

bash 复制代码
npm init -y
npm install electron electron-builder --save-dev

接下来,让我们创建项目的基本文件结构:

  1. 首先创建 main.js 文件,这是 Electron 主进程的入口文件:
javascript:main.js 复制代码
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const path = require('path');
const fs = require('fs');
const https = require('https');
const http = require('http');

let mainWindow;

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

  mainWindow.loadFile('index.html');
  mainWindow.on('closed', () => {
    mainWindow = null;
  });
}

app.whenReady().then(createWindow);

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

app.on('activate', () => {
  if (mainWindow === null) {
    createWindow();
  }
});

// 文件下载功能
ipcMain.handle('download-file', async (event, { url, fileName }) => {
  try {
    const result = await dialog.showSaveDialog(mainWindow, {
      defaultPath: fileName,
    });

    if (result.canceled) {
      return { success: false, message: '用户取消了下载' };
    }

    const filePath = result.filePath;
    
    return new Promise((resolve) => {
      try {
        // Determine which protocol module to use based on the URL
        const protocol = url.startsWith('https:') ? https : url.startsWith('http:') ? http : null;
        
        if (!protocol) {
          resolve({ success: false, message: '不支持的协议,仅支持 HTTP 和 HTTPS' });
          return;
        }
        
        protocol.get(url, (response) => {
          const file = fs.createWriteStream(filePath);
          response.pipe(file);
          
          file.on('finish', () => {
            file.close();
            resolve({ success: true, filePath });
          });
          
          file.on('error', (err) => {
            fs.unlink(filePath, () => {});
            resolve({ success: false, message: err.message });
          });
        }).on('error', (err) => {
          resolve({ success: false, message: err.message });
        });
      } catch (error) {
        resolve({ success: false, message: error.message });
      }
    });
  } catch (error) {
    return { success: false, message: error.message };
  }
});

// 文件上传功能
ipcMain.handle('upload-file', async (event) => {
  try {
    const result = await dialog.showOpenDialog(mainWindow, {
      properties: ['openFile'],
      filters: [
        { name: '所有文件', extensions: ['*'] }
      ]
    });

    if (result.canceled || result.filePaths.length === 0) {
      return { success: false, message: '用户取消了上传' };
    }

    const filePath = result.filePaths[0];
    const fileName = path.basename(filePath);
    const fileContent = fs.readFileSync(filePath);

    // 这里是上传示例,实际应用中应该替换为实际的上传逻辑
    // 例如使用 axios 或 fetch 发送到服务器
    
    // 模拟上传成功
    return { 
      success: true, 
      fileName, 
      filePath, 
      fileSize: fileContent.length 
    };
  } catch (error) {
    return { success: false, message: error.message };
  }
});
  1. 创建 preload.js 文件,用于在主进程和渲染进程之间建立安全的通信通道:
javascript:preload.js 复制代码
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  downloadFile: (params) => ipcRenderer.invoke('download-file', params),
  uploadFile: () => ipcRenderer.invoke('upload-file')
});
  1. 创建 index.html 文件,这是应用的主界面:
html:index.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 文件上传下载示例</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      margin: 40px;
      text-align: center;
    }
    .container {
      max-width: 600px;
      margin: 0 auto;
    }
    h1 {
      color: #333;
    }
    .form-group {
      margin-bottom: 20px;
      text-align: left;
    }
    label {
      display: block;
      margin-bottom: 5px;
      font-weight: 500;
    }
    input, button {
      width: 100%;
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 4px;
      font-size: 16px;
      box-sizing: border-box;
    }
    button {
      background-color: #4CAF50;
      color: white;
      border: none;
      cursor: pointer;
      margin-top: 10px;
    }
    button:hover {
      background-color: #45a049;
    }
    .upload-btn {
      background-color: #2196F3;
    }
    .upload-btn:hover {
      background-color: #0b7dda;
    }
    #status {
      margin-top: 20px;
      padding: 10px;
      border-radius: 4px;
      background-color: #f5f5f5;
      min-height: 20px;
    }
    .success {
      color: green;
    }
    .error {
      color: red;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>Electron 文件上传下载示例</h1>
    
    <div class="form-group">
      <label for="download-url">下载文件 URL:</label>
      <input type="text" id="download-url" placeholder="请输入文件的下载链接">
      <button id="download-btn">下载文件</button>
    </div>
    
    <div class="form-group">
      <label for="file-name">保存的文件名:</label>
      <input type="text" id="file-name" placeholder="请输入要保存的文件名">
    </div>
    
    <div class="form-group">
      <button id="upload-btn" class="upload-btn">上传文件</button>
    </div>
    
    <div id="status"></div>
  </div>

  <script>
    document.getElementById('download-btn').addEventListener('click', async () => {
      const url = document.getElementById('download-url').value;
      const fileName = document.getElementById('file-name').value || 'downloaded-file';
      const statusElement = document.getElementById('status');
      
      if (!url) {
        statusElement.textContent = '请输入文件的下载链接';
        statusElement.className = 'error';
        return;
      }
      
      statusElement.textContent = '正在下载...';
      statusElement.className = '';
      
      try {
        const result = await window.electronAPI.downloadFile({ url, fileName });
        if (result.success) {
          statusElement.textContent = `文件下载成功: ${result.filePath}`;
          statusElement.className = 'success';
        } else {
          statusElement.textContent = `下载失败: ${result.message}`;
          statusElement.className = 'error';
        }
      } catch (error) {
        statusElement.textContent = `发生错误: ${error.message}`;
        statusElement.className = 'error';
      }
    });
    
    document.getElementById('upload-btn').addEventListener('click', async () => {
      const statusElement = document.getElementById('status');
      statusElement.textContent = '请选择要上传的文件...';
      statusElement.className = '';
      
      try {
        const result = await window.electronAPI.uploadFile();
        if (result.success) {
          statusElement.textContent = 
            `文件上传成功: ${result.fileName} (${(result.fileSize / 1024).toFixed(2)} KB)`;
          statusElement.className = 'success';
          
          // 在实际应用中,这里可以添加更多的上传后处理逻辑
        } else {
          statusElement.textContent = `上传失败: ${result.message}`;
          statusElement.className = 'error';
        }
      } catch (error) {
        statusElement.textContent = `发生错误: ${error.message}`;
        statusElement.className = 'error';
      }
    });
  </script>
</body>
</html>
  1. 更新 package.json 文件,添加启动脚本和应用信息:
json:package.json 复制代码
{
  "name": "electron-file-transfer",
  "version": "1.0.0",
  "description": "Electron 文件上传下载示例应用",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "build": "electron-builder"
  },
  "keywords": ["electron", "file", "upload", "download"],
  "author": "Your Name",
  "license": "MIT",
  "devDependencies": {
    "electron": "^25.0.0",
    "electron-builder": "^24.6.3"
  },
  "build": {
    "appId": "com.example.electronfiletransfer",
    "productName": "Electron文件传输",
    "win": {
      "target": "nsis",
      "icon": "build/icon.ico"
    },
    "mac": {
      "target": "dmg",
      "icon": "build/icon.icns"
    },
    "linux": {
      "target": "AppImage"
    }
  }
}

现在,项目的基本文件已经创建完成。让我们创建一个 .gitignore 文件(如果需要的话):

:.gitignore 复制代码
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

项目创建完成后,您可以通过以下命令运行应用:

bash 复制代码
npm start

如果需要打包应用,可以使用:

bash 复制代码
npm run build

这个 Electron 应用实现了以下功能:

  1. 文件下载:用户可以输入文件的 URL 和保存的文件名,点击下载按钮后,应用会通过 Electron 的对话框让用户选择保存位置,然后下载文件。

  2. 文件上传:用户点击上传按钮后,应用会弹出文件选择对话框,用户选择文件后,应用会读取文件内容并模拟上传过程。在实际应用中,您需要将模拟上传的部分替换为实际的服务器上传逻辑。

这个项目使用了 Electron 的现代安全实践,如 contextIsolation 和 preload.js,确保了应用的安全性。

相关推荐
人机888号3 分钟前
instanceof 的小秘密
javascript
轻语呢喃3 分钟前
时间分片思想:多数据的前端处理方法
前端·javascript·面试
Spider_Man3 分钟前
假装渲染十万条,虚拟列表的障眼法你学会了吗?
前端·javascript·react.js
334554329 分钟前
vue实现表格轮播
javascript·vue.js·ecmascript
诗书画唱2 小时前
【前端教程】从零开始学JavaScript交互:7个经典事件处理案例解析
前端·javascript·交互
LikM2 小时前
Reflect ES6 新增的内置对象
前端·javascript
艾小码2 小时前
还在被JavaScript数据类型搞糊涂?一篇文章帮你彻底搞懂!
前端·javascript
码力无边_OEC2 小时前
第五章:组件间的“密语”—— Messaging 通信机制
前端·javascript
子兮曰2 小时前
🌌 七夕故事之从牛郎织女到JavaScript:10个编程哲学思考
javascript
砂糖橘加盐2 小时前
vapor 性能测试和基本原理浅析
前端·javascript·vue.js