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