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);
})

浙公网安备 33010602011771号