electron + vue3 备份数据库压缩传输到ftp服务器

1. 写bat文件

echo Dumping database 2024_suntory ...

set PATH=%PATH%;C:\Program Files\MySQL\MySQL Server 5.7\bin
set PATH=%PATH%;C:\Program Files\WinRAR
 
"C:\Program Files\MySQL\MySQL Server 5.7\bin\mysqldump" -uroot -p123456 --single-transaction hanyun > D:\dbbk\db1.sql 2>nul

del /F /S /Q D:\dbbk\db1.rar 2>nul
  
WinRAR a -p123456  -k -r -s -m1 -ep1 D:\dbbk\db1.rar D:\dbbk\db1.sql 2>nul

del /F /S /Q D:\dbbk\db1.sql 2>nul

echo Done

:exit

2.需要装 MYSQL WinRAR 并且配置环境变量

3.封装 备份压缩和ftp传输函数 ftp.js

 const ftp = require('ftp')
const fs = require('fs')  
const { spawn } = require('child_process')

let address = 'D:/dbbk/'
if(localStorage.getItem('address')){
  address = localStorage.getItem('address')
}else{
  localStorage.setItem('address',address)
}
export function uploadFile(line) {  
  return new Promise((resolve, reject) => {      
    let config = { host: '192.168.10.100',user:'ftp',password:'ftp123456'}
    if(localStorage.getItem('FTPConfig')){
      config = JSON.parse(localStorage.getItem('FTPConfig'))
    }else{
      localStorage.setItem('FTPConfig',JSON.stringify(config))
    } 
    try{
      const localFilePath = `${address}db${line}.rar` // 替换为您的RAR文件路径  
      const remoteFilePath = `/db${line}.rar` // 替换为您希望保存在服务器上的路径和文件名 
      const client = new ftp()
      client.log = console.log // 这行是可选的,用于输出FTP客户端的日志信息 
      client.connect(config) 
      client.on('error', () => {
        reject('数据库备份失败')
      })
      client.on('ready', () => {
        console.log('已连接到FTP服务器') 
        // 使用createReadStream从本地文件系统读取文件流
        const readStream = fs.createReadStream(localFilePath)
      
        // 发送STOR命令上传文件到FTP服务器
        client.put(readStream, remoteFilePath, (err) => {
          if (err) {
            reject('文件上传失败')
            console.error('文件上传失败:', err)
            client.end()
          } else {
            resolve('文件上传成功')
            console.log('文件上传成功')
            client.end()
          }
        }) 
        // 监听end事件,确保连接正确关闭
        readStream.on('end', () => {
          console.log('文件流已结束')
        })
      }) 
      
    }catch(e){ 
      reject('ftp 连接失败')
    } 
  }) 
}



export function handleBat(line) { 
  return new Promise((resolve, reject) => { 
    const batFilePath = `${address}db${line}.bat` 

    const batScript = spawn('cmd', ['/c', batFilePath])
    console.log(batScript)
  
    batScript.stdout.on('data', (data) => {
      // console.log(`标准输出: ${data}`)
    })
  
    batScript.stderr.on('data', (data) => {
      console.error(`错误输出: ${data}`)
      // reject('执行失败')
    })
  
    batScript.on('close', (code) => {
      console.log(`子进程退出码: ${code}`)
      resolve('执行成功')
    })
  })
}
 

 4.按钮触发 测试直接写到了App.vue

<template>
  <div id="app">   
    <el-button type="primary" @click="handleBatFun">数据上传</el-button>
   
  </div>
</template> 
<script>     
  
import {handleBat,uploadFile} from './utils/ftp'
import { ElLoading ,ElMessage} from 'element-plus'
export default {
  name: 'ElectronVueElementAdmin', 
  components: { 
  }, 
  data(){
    return{
      loadingInstance:""
    }
  },
  
  watch:{
   
  },
  created(){    
  }, 
  methods:{ 
    showLoading() { 
      this.loadingInstance = ElLoading.service({
        text: '数据库压缩中,请稍等',
        fullscreen: true,
        background: 'rgba(0, 0, 0, 0.8)' 
      }) 
      setTimeout(()=>{
        if(this.loadingInstance){
          this.closeLoading()
          ElMessage({
            message: "长时间未完成,请重新操作",
            type: "error",
            duration:5000
          })
        }
      },1000*60*20)
    },
    closeLoading(){
      this.loadingInstance?.close()
      this.loadingInstance = null
    },
    handleBatFun(){
      this.showLoading()
      let line = localStorage.getItem('productLine') || 1
      handleBat(line).then(()=>{
        ElMessage({
          type:"success",
          message:"压缩成功"
        })
        this.loadingInstance.setText('数据库压缩包上传中')
        this.uploadFileFun()
      }).catch(()=>{
        ElMessage({
          type:"error",
          message:"失败"
        })
        this.closeLoading()
      })
    },
    uploadFileFun(){
      let line = localStorage.getItem('productLine') || 1
      uploadFile(line).then(()=>{
        ElMessage({
          type:"success",
          message:"上传成功"
        })
      }).catch((e)=>{
        ElMessage({
          type:"error",
          message:e || "上传失败"
        })
      }).finally(()=>{
        this.closeLoading()
      })
    },
  }
}
</script>

<style> 
</style>

  5.注意在git拉取的一个项目electron-vue-template-master 没有引入element-plus 配置vite.config.js

const Path = require('path');
const vuePlugin = require('@vitejs/plugin-vue')

const { defineConfig } = require('vite');
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";

/**
 * https://vitejs.dev/config
 */
const config = defineConfig({ 
    root: Path.join(__dirname, 'src', 'renderer'),
    publicDir: 'public',
    server: {
        port: 8080,
    },
    open: false,
    build: {
        outDir: Path.join(__dirname, 'build', 'renderer'),
        emptyOutDir: true,
    },
    plugins: [
        vuePlugin(),
        AutoImport({
            resolvers: [ElementPlusResolver()],
        }),
        Components({
            resolvers: [ElementPlusResolver()],
        }),
    ],
});

module.exports = config;

6.注意开发环境没问题打包报错 安装依赖  "xregexp": "^2.0.0" ,打包回提示下载一个rar 放到eletron 目录即可 我的是 C:\Users\Administrator\AppData\Local\electron\Cache

7.mian.ts 配一个 空preload

import {app, BrowserWindow, ipcMain, session} from 'electron';
import {join} from 'path';

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

  if (process.env.NODE_ENV === 'development') {
    const rendererPort = process.argv[2];
    mainWindow.loadURL(`http://localhost:${rendererPort}`);
  }
  else {
    mainWindow.loadFile(join(app.getAppPath(), 'renderer', 'index.html'));
  }
}

app.whenReady().then(() => {
  createWindow();

  session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
    callback({
      responseHeaders: {
        ...details.responseHeaders,
        'Content-Security-Policy': ['script-src \'self\'']
      }
    })
  })

  app.on('activate', function () {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
});

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

ipcMain.on('message', (event, message) => {
  console.log(message);
})

  

 

posted @ 2025-04-24 17:19  福超  阅读(34)  评论(0)    收藏  举报