529 lines
16 KiB
JavaScript
529 lines
16 KiB
JavaScript
|
import SparkMD5 from 'spark-md5'
|
|||
|
const fs = require('fs')
|
|||
|
const path = require('path')
|
|||
|
import { ElectronDownloadManager } from 'electron-dl-manager'
|
|||
|
import { dialog } from 'electron'
|
|||
|
import axios from 'axios'
|
|||
|
const uploadUrl = import.meta.env.VITE_APP_UPLOAD_API + '/smarttalk/file/upload'
|
|||
|
const asyncUploadUrl = import.meta.env.VITE_APP_UPLOAD_API + '/smarttalk/file/asyncUpload'
|
|||
|
const manager = new ElectronDownloadManager()
|
|||
|
export default async function ({ app, shell, BrowserWindow, ipcMain }) {
|
|||
|
const userDataPath = app.getPath('userData')
|
|||
|
const appRootFilePath = userDataPath + '\\selfFile\\'
|
|||
|
const appTempFilePath = userDataPath + '\\tempFile\\'
|
|||
|
let Spark = new SparkMD5.ArrayBuffer()
|
|||
|
|
|||
|
ipcMain.on('upload-file-change', (e, { id, fileNewName, cookie, fileType }) => {
|
|||
|
let filePath = appRootFilePath + fileNewName
|
|||
|
//执行更新,上传文件
|
|||
|
let formData = new FormData()
|
|||
|
formData.append('id', id)
|
|||
|
uploadFileByFS({
|
|||
|
url: asyncUploadUrl,
|
|||
|
path: filePath,
|
|||
|
name: fileNewName,
|
|||
|
cookie,
|
|||
|
fileType,
|
|||
|
formData,
|
|||
|
success: (response) => {
|
|||
|
e.reply('upload-file-change-success' + fileNewName, {
|
|||
|
data: response.data,
|
|||
|
md5: formData.md5
|
|||
|
})
|
|||
|
},
|
|||
|
error: (err) => {
|
|||
|
console.error('Error uploading file:', err)
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
/*监听文件改变,如果有改变则返回触发*/
|
|||
|
ipcMain.on('listen-file-change', (e, { id, fileNewName, md5, cookie, fileType }) => {
|
|||
|
let filePath = appRootFilePath + fileNewName
|
|||
|
let uploadId = null
|
|||
|
let isOn = false
|
|||
|
let lastMTime = fs.statSync(filePath).mtime.getTime()
|
|||
|
console.log(lastMTime)
|
|||
|
setInterval(() => {
|
|||
|
getFileMsg(filePath).then((msg) => {
|
|||
|
if (msg !== lastMTime) {
|
|||
|
lastMTime = msg
|
|||
|
if (uploadId) {
|
|||
|
clearTimeout(uploadId)
|
|||
|
}
|
|||
|
if (isOn === false) {
|
|||
|
console.log(fileNewName)
|
|||
|
e.reply('listen-file-change-on' + fileNewName)
|
|||
|
isOn = true
|
|||
|
}
|
|||
|
//倒数十秒提交更改,十秒之内有继续修改则重置倒数
|
|||
|
uploadId = setTimeout(() => {
|
|||
|
console.log(223)
|
|||
|
//执行更新,上传文件
|
|||
|
let formData = new FormData()
|
|||
|
formData.append('id', id)
|
|||
|
uploadFileByFS({
|
|||
|
url: asyncUploadUrl,
|
|||
|
path: filePath,
|
|||
|
name: fileNewName,
|
|||
|
cookie,
|
|||
|
fileType,
|
|||
|
formData,
|
|||
|
success: (response) => {
|
|||
|
e.reply('listen-file-change-success' + fileNewName, {
|
|||
|
data: response.data,
|
|||
|
md5: formData.md5
|
|||
|
})
|
|||
|
clearTimeout(uploadId)
|
|||
|
isOn = false
|
|||
|
},
|
|||
|
error: (err) => {
|
|||
|
console.error('Error uploading file:', err)
|
|||
|
}
|
|||
|
})
|
|||
|
}, 5000)
|
|||
|
}
|
|||
|
})
|
|||
|
}, 1000)
|
|||
|
})
|
|||
|
|
|||
|
function getFileMsg(path) {
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
const stats = fs.statSync(path)
|
|||
|
return resolve(stats.mtime.getTime())
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
function getFileMD5(path) {
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
fs.readFile(path, (err, dataFile) => {
|
|||
|
if (err) {
|
|||
|
reject(err)
|
|||
|
return console.error(err)
|
|||
|
}
|
|||
|
Spark.append(dataFile)
|
|||
|
let md5 = Spark.end()
|
|||
|
resolve(md5)
|
|||
|
})
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* 判断是否有本地文件
|
|||
|
* */
|
|||
|
ipcMain.on('is-have-local-file', (e, fileNewName) => {
|
|||
|
let filePath = appRootFilePath + fileNewName
|
|||
|
fs.access(filePath, fs.constants.F_OK, (err) => {
|
|||
|
if (err) {
|
|||
|
e.reply('is-have-local-file-reply' + fileNewName, false)
|
|||
|
return
|
|||
|
}
|
|||
|
e.reply('is-have-local-file-reply' + fileNewName, true)
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
/*
|
|||
|
* 判断是需要同步本地文件
|
|||
|
* */
|
|||
|
ipcMain.on('is-async-local-file', (e, { fileNewName, lastModifyTime, md5 }) => {
|
|||
|
let filePath = appRootFilePath + fileNewName
|
|||
|
fs.access(filePath, fs.constants.F_OK, (err) => {
|
|||
|
if (err) {
|
|||
|
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'down' })
|
|||
|
return
|
|||
|
}
|
|||
|
getFileMsg(filePath).then((msg) => {
|
|||
|
let time = new Date(lastModifyTime).getTime();
|
|||
|
msg = parseInt(msg/1000)*1000;
|
|||
|
if (msg == time) {
|
|||
|
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: false, type: '' })
|
|||
|
} else {
|
|||
|
const stats = fs.statSync(filePath)
|
|||
|
//如果线上时间大于线下时间,就需要从线上下载,否则则需要上传
|
|||
|
if (time > stats.mtime.getTime()) {
|
|||
|
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'down' })
|
|||
|
} else if (time < stats.mtime.getTime()) {
|
|||
|
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'upload' })
|
|||
|
}
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
//默认浏览器打开url
|
|||
|
ipcMain.on('open-url-browser', (e, url) => {
|
|||
|
shell.openPath(url)
|
|||
|
})
|
|||
|
|
|||
|
//使用默认应用打开本地文件
|
|||
|
ipcMain.on('open-path-app', (e, destination) => {
|
|||
|
let path = appRootFilePath + destination
|
|||
|
shell.openPath(path).catch((error) => {
|
|||
|
console.log(error)
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
//复制文件
|
|||
|
ipcMain.on('copy-file-default', (e, { source, destination }) => {
|
|||
|
copyFile(source, destination, (error, filePath) => {
|
|||
|
e.reply('copy-file-default-reply', { error, filePath })
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
//导出文件
|
|||
|
ipcMain.on('export-file-default', (e, list) => {
|
|||
|
exportFile(list, (res) => {
|
|||
|
e.reply('export-file-default-reply', res)
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
function uploadFileByFS({ url, path, name, cookie, fileType, formData, success, error }) {
|
|||
|
fs.readFile(path, (err, data) => {
|
|||
|
if (err) {
|
|||
|
return console.error(err)
|
|||
|
}
|
|||
|
// 配置上传的请求
|
|||
|
const config = {
|
|||
|
headers: {
|
|||
|
'Content-Type': 'multipart/form-data', // 或者其他适合上传文件的Content-Type
|
|||
|
Authorization: 'Bearer ' + cookie
|
|||
|
}
|
|||
|
}
|
|||
|
Spark.append(data)
|
|||
|
let md5 = Spark.end()
|
|||
|
// 使用axios上传文件
|
|||
|
let file = new File([data], name, {
|
|||
|
type: fileType
|
|||
|
})
|
|||
|
const stats = fs.statSync(path)
|
|||
|
formData.append('file', file)
|
|||
|
formData.append('md5', md5)
|
|||
|
formData.append('lastModifyTime', stats.mtime.toLocaleString())
|
|||
|
axios
|
|||
|
.post(url, formData, config)
|
|||
|
.then((response) => {
|
|||
|
success(response)
|
|||
|
})
|
|||
|
.catch((errorMsg) => {
|
|||
|
error(errorMsg)
|
|||
|
})
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
/*创建新的ppt文件*/
|
|||
|
ipcMain.on('creat-file-default', (e, { name, uploadData, cookie }) => {
|
|||
|
createFolder('tempFile').then(() => {
|
|||
|
let path = appTempFilePath + name.replace(/[\\/:*?"<>|]/, '')
|
|||
|
console.log(path)
|
|||
|
fs.writeFileSync(path, '', 'utf-8')
|
|||
|
let fileType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
|||
|
let formData = new FormData()
|
|||
|
for (let key in uploadData) {
|
|||
|
if (Object.prototype.hasOwnProperty.call(uploadData, key)) {
|
|||
|
// 检查是否是对象自身的属性
|
|||
|
formData.append(key, uploadData[key])
|
|||
|
}
|
|||
|
}
|
|||
|
formData.append('fileFlag', '课件')
|
|||
|
uploadFileByFS({
|
|||
|
url: uploadUrl,
|
|||
|
path,
|
|||
|
name,
|
|||
|
cookie,
|
|||
|
fileType,
|
|||
|
formData,
|
|||
|
success: (response) => {
|
|||
|
e.reply('creat-file-default-reply', response.data)
|
|||
|
console.log('File uploaded successfully:', response.data)
|
|||
|
},
|
|||
|
error: (err) => {
|
|||
|
console.error('Error uploading file:', err)
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
/*创建新的ppt文件*/
|
|||
|
ipcMain.on('creat-ai-file-default', (e, { name, url, uploadData, cookie }) => {
|
|||
|
createFolder('tempFile').then(async () => {
|
|||
|
let lastname = decodeURIComponent(url);
|
|||
|
name = lastname.substring(lastname.lastIndexOf("/")+1)
|
|||
|
let path = appTempFilePath + name.replace(/[\\/:*?"<>|]/, '')
|
|||
|
let {type,item} = await downloadFiles(url,name)
|
|||
|
if (type==="成功") {
|
|||
|
let fileType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
|||
|
let formData = new FormData()
|
|||
|
for (let key in uploadData) {
|
|||
|
if (Object.prototype.hasOwnProperty.call(uploadData, key)) {
|
|||
|
// 检查是否是对象自身的属性
|
|||
|
formData.append(key, uploadData[key])
|
|||
|
}
|
|||
|
}
|
|||
|
formData.append('fileFlag', '课件')
|
|||
|
uploadFileByFS({
|
|||
|
url: uploadUrl,
|
|||
|
path,
|
|||
|
name,
|
|||
|
cookie,
|
|||
|
fileType,
|
|||
|
formData,
|
|||
|
success: (response) => {
|
|||
|
e.reply('creat-ai-file-default-reply', response.data)
|
|||
|
console.log('File uploaded successfully:', response.data)
|
|||
|
},
|
|||
|
error: (err) => {
|
|||
|
console.error('Error uploading file:', err)
|
|||
|
}
|
|||
|
})
|
|||
|
}else {
|
|||
|
e.reply('creat-ai-file-default-reply', type)
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
function downloadFiles(url,fileName) {
|
|||
|
console.log(url,fileName)
|
|||
|
return new Promise((resolve, reject)=>{
|
|||
|
const browserWindow = BrowserWindow.getFocusedWindow()
|
|||
|
const id = manager.download({
|
|||
|
window: browserWindow,
|
|||
|
url: url,
|
|||
|
saveAsFilename: fileName,
|
|||
|
directory: appTempFilePath,
|
|||
|
callbacks: {
|
|||
|
onDownloadStarted: async ({ id, item, webContents }) => {
|
|||
|
// Do something with the download id
|
|||
|
},
|
|||
|
onDownloadProgress: async ({ id, item, percentCompleted }) => {
|
|||
|
// console.log(percentCompleted)
|
|||
|
},
|
|||
|
onDownloadCompleted: async ({ id, item }) => {
|
|||
|
console.log('完成')
|
|||
|
resolve({type:"成功",item})
|
|||
|
},
|
|||
|
onDownloadCancelled: async () => {
|
|||
|
console.log("取消")
|
|||
|
reject({type:"取消了下载"})
|
|||
|
},
|
|||
|
onDownloadInterrupted: async () => {
|
|||
|
console.log('中断')
|
|||
|
reject({type:"下载被中断"})
|
|||
|
},
|
|||
|
onError: (err, data) => {
|
|||
|
console.log(err.toString())
|
|||
|
reject({type:"下载出错",err})
|
|||
|
}
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
//获取应用文件目录
|
|||
|
ipcMain.on('get-root-file-path', (e) => {
|
|||
|
e.reply('get-root-file-path-reply', appRootFilePath)
|
|||
|
})
|
|||
|
|
|||
|
//下载文件
|
|||
|
ipcMain.on('download-file-default', (e, { url, fileName }) => {
|
|||
|
createFolder('selfFile')
|
|||
|
.then(async () => {
|
|||
|
const browserWindow = BrowserWindow.getFocusedWindow()
|
|||
|
const id = await manager.download({
|
|||
|
window: browserWindow,
|
|||
|
url: url,
|
|||
|
saveAsFilename: fileName,
|
|||
|
directory: appRootFilePath,
|
|||
|
callbacks: {
|
|||
|
onDownloadStarted: async ({ id, item, webContents }) => {
|
|||
|
// Do something with the download id
|
|||
|
},
|
|||
|
onDownloadProgress: async ({ id, item, percentCompleted }) => {
|
|||
|
e.reply('download-file-default-prog' + fileName, percentCompleted)
|
|||
|
},
|
|||
|
onDownloadCompleted: async ({ id, item }) => {
|
|||
|
console.log('完成')
|
|||
|
e.reply('download-file-default' + fileName, true)
|
|||
|
},
|
|||
|
onDownloadCancelled: async () => {
|
|||
|
console.log('取消')
|
|||
|
e.reply('download-file-default' + fileName, false)
|
|||
|
},
|
|||
|
onDownloadInterrupted: async () => {
|
|||
|
console.log('中断')
|
|||
|
e.reply('download-file-default' + fileName, false)
|
|||
|
},
|
|||
|
onError: (err, data) => {
|
|||
|
console.log(err.toString())
|
|||
|
e.reply('download-file-default' + fileName, false)
|
|||
|
}
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
.catch((error) => {
|
|||
|
e.reply('download-file-default' + fileName, false)
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
/**另存为...
|
|||
|
* 接收渲染进程 保存文件的 的通知
|
|||
|
* @param {Object} event
|
|||
|
* @param {String} url 下载链接
|
|||
|
* @param {String} fileName 文件名称包括后缀名,例如图1.png
|
|||
|
*/
|
|||
|
ipcMain.on('save-as', function (event, url, fileName) {
|
|||
|
let win = BrowserWindow.getFocusedWindow()
|
|||
|
//通过扩展名识别文件类型
|
|||
|
let filters = [{ name: '全部文件', extensions: ['*'] }]
|
|||
|
let ext = path.extname(fileName) //获取扩展名
|
|||
|
if (ext && ext !== '.') {
|
|||
|
const name = ext.slice(1, ext.length)
|
|||
|
if (name) {
|
|||
|
filters.unshift({
|
|||
|
name: '',
|
|||
|
extensions: [name]
|
|||
|
})
|
|||
|
}
|
|||
|
}
|
|||
|
let filePath = null //用户选择存放文件的路径
|
|||
|
|
|||
|
//1- 弹出另存为弹框,用于获取保存路径
|
|||
|
dialog
|
|||
|
.showSaveDialog(win, {
|
|||
|
title: '另存为',
|
|||
|
filters,
|
|||
|
defaultPath: fileName
|
|||
|
})
|
|||
|
.then((result) => {
|
|||
|
//点击保存后开始下载
|
|||
|
filePath = result.filePath
|
|||
|
if (filePath) {
|
|||
|
win.webContents.downloadURL(url) // 触发will-download事件
|
|||
|
}
|
|||
|
})
|
|||
|
.catch(() => {
|
|||
|
console.log('另存为--catch')
|
|||
|
})
|
|||
|
|
|||
|
//2- 准备下载的时候触发
|
|||
|
win.webContents.session.once('will-download', (event, item, webContents) => {
|
|||
|
if (!filePath) return
|
|||
|
//设置下载项的保存文件路径
|
|||
|
item.setSavePath(filePath)
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
/*导出文件*/
|
|||
|
function exportFile(list, callback) {
|
|||
|
let win = BrowserWindow.getFocusedWindow()
|
|||
|
//通过扩展名识别文件类型
|
|||
|
let filePath = null //用户选择存放文件的路径
|
|||
|
//1- 弹出另存为弹框,用于获取保存路径
|
|||
|
dialog
|
|||
|
.showOpenDialog(win, {
|
|||
|
properties: ['openDirectory']
|
|||
|
})
|
|||
|
.then(async (result) => {
|
|||
|
if (result.filePaths[0]) {
|
|||
|
filePath = result.filePaths[0]
|
|||
|
let res = []
|
|||
|
for (let i = 0; i < list.length; i++) {
|
|||
|
let item = list[i]
|
|||
|
let source = appRootFilePath + item.id
|
|||
|
let destination = filePath + '/' + item.name
|
|||
|
await copyRelFile(source, filterCopyFile(destination), (error, path) => {
|
|||
|
res.push({ error, path })
|
|||
|
})
|
|||
|
}
|
|||
|
callback(res)
|
|||
|
}
|
|||
|
})
|
|||
|
.catch(() => {
|
|||
|
console.log('另存为--catch')
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
/*文件是否已经存在*/
|
|||
|
function isHaveFile(path) {
|
|||
|
return fs.existsSync(path)
|
|||
|
}
|
|||
|
|
|||
|
/*判断是否已经存在这个名字的文件,如果已经存在则递增导出*/
|
|||
|
function filterCopyFile(path, index = 0) {
|
|||
|
if (isHaveFile(path) === true) {
|
|||
|
index++
|
|||
|
path = path.replaceAll('.', `(${index}).`)
|
|||
|
return filterCopyFile(path, index)
|
|||
|
} else {
|
|||
|
return path
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*复制文件*/
|
|||
|
function copyRelFile(source, destination, callback) {
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
const readStream = fs.createReadStream(source)
|
|||
|
const writeStream = fs.createWriteStream(destination)
|
|||
|
|
|||
|
readStream.on('error', (error) => {
|
|||
|
reject()
|
|||
|
callback(error, null)
|
|||
|
})
|
|||
|
writeStream.on('error', (error) => {
|
|||
|
reject()
|
|||
|
callback(error, null)
|
|||
|
})
|
|||
|
writeStream.on('close', () => {
|
|||
|
console.log('关闭写入流')
|
|||
|
callback(null, destination)
|
|||
|
resolve()
|
|||
|
})
|
|||
|
readStream.pipe(writeStream)
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
/*复制文件*/
|
|||
|
function copyFile(source, destination, callback) {
|
|||
|
let path = appRootFilePath + destination
|
|||
|
createFolder('selfFile').then(() => {
|
|||
|
const readStream = fs.createReadStream(source)
|
|||
|
const writeStream = fs.createWriteStream(path)
|
|||
|
|
|||
|
readStream.on('error', (error) => {
|
|||
|
callback(error, null)
|
|||
|
})
|
|||
|
writeStream.on('error', (error) => {
|
|||
|
callback(error, null)
|
|||
|
})
|
|||
|
writeStream.on('close', () => {
|
|||
|
callback(null, path)
|
|||
|
})
|
|||
|
|
|||
|
readStream.pipe(writeStream)
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
/*创建文件夹*/
|
|||
|
function createFolder(folderName) {
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
const folderPath = path.join(userDataPath, folderName)
|
|||
|
// 异步检查文件夹是否存在,不存在则创建
|
|||
|
fs.access(folderPath, fs.constants.F_OK, (err) => {
|
|||
|
if (err) {
|
|||
|
fs.mkdir(folderPath, { recursive: true }, (mkdirErr) => {
|
|||
|
if (mkdirErr) {
|
|||
|
console.error(mkdirErr)
|
|||
|
reject()
|
|||
|
} else {
|
|||
|
console.log(`Folder ${folderName} created successfully.`)
|
|||
|
resolve()
|
|||
|
}
|
|||
|
})
|
|||
|
} else {
|
|||
|
console.log(`Folder ${folderName} already exists.`)
|
|||
|
resolve()
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
}
|
|||
|
}
|