diff --git a/elsrc/TopMenu.js b/elsrc/TopMenu.js index 006d8bd..f182679 100644 --- a/elsrc/TopMenu.js +++ b/elsrc/TopMenu.js @@ -99,6 +99,23 @@ exports.menuRebuild = (mainWindow) => { } } ] + }, + { + label: '云服务', + submenu: [ + { + label: '同步上传', + click: async () => { + await shell.openExternal('http://www.huaruyu.com') + } + }, + { + label: '同步下载', + click: async () => { + await shell.openExternal('http://www.huaruyu.com') + } + } + ] } ]; } diff --git a/elsrc/sync/tencent/UploadUtils.js b/elsrc/sync/tencent/UploadUtils.js index f20d464..47f55fe 100644 --- a/elsrc/sync/tencent/UploadUtils.js +++ b/elsrc/sync/tencent/UploadUtils.js @@ -1,18 +1,58 @@ const COS = require('cos-nodejs-sdk-v5'); -const pathOpt = require("path"); -const cos = new COS({ - SecretId: 'AKIDvjKhqrfEaliRq11nMcrGZmsATiyNl1BA', - // 推荐使用环境变量获取;用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。 - // 子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140 - SecretKey: 'xpZCjCTVJzZG2wyy8mFVwLWTVVIqAKct', - Domain: 'https://note-1324909903.cos.ap-beijing.myqcloud.com' -}); -const Bucket='note-1324909903' -const Region = 'ap-beijing' +const {dialog} = require('electron') +const {readFileSync,createWriteStream}=require('node:fs') +const md5 = require("md5"); class UploadUtils { + + static cos = new COS({ + SecretId: 'AKIDvjKhqrfEaliRq11nMcrGZmsATiyNl1BA', + // 推荐使用环境变量获取;用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。 + // 子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140 + SecretKey: 'xpZCjCTVJzZG2wyy8mFVwLWTVVIqAKct', + Domain: 'https://note-1324909903.cos.ap-beijing.myqcloud.com' + }); + static Bucket='note-1324909903' + static Region = 'ap-beijing' + constructor(store) { this.store = store; } + static selfUploadFile(activeFile){ + console.log("cos.uploadFile") + UploadUtils.cos.uploadFile({ + Bucket: UploadUtils.Bucket, /* 填入您自己的存储桶,必须字段 */ + Region: UploadUtils.Region, /* 存储桶所在地域,例如 ap-beijing,必须字段 */ + Key: activeFile, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */ + FilePath: activeFile, /* 必须 */ + SliceSize: 1024 * 1024 * 5, /* 触发分块上传的阈值,超过5MB使用分块上传,非必须 */ + onTaskReady: function (taskId) { /* 非必须 */ + console.log(taskId); + }, + onProgress: function (progressData) { /* 非必须 */ + console.log(JSON.stringify(progressData)); + }, + onFileFinish: function (err, data, options) { /* 非必须 */ + console.log(options.Key + '上传' + (err ? '失败' : '完成')); + }, + // 支持自定义headers 非必须 + }, function (err, data) { + console.log(err || data); + }); + } + + static selfDownLoadFile(activeFile){ + console.log("cos.downloadFile",activeFile) + UploadUtils.cos.getObject({ + Bucket: UploadUtils.Bucket, /* 填入您自己的存储桶,必须字段 */ + Region: UploadUtils.Region, /* 存储桶所在地域,例如 ap-beijing,必须字段 */ + Key: activeFile, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */ + Output: activeFile + // 支持自定义headers 非必须 + }, function (err, data) { + console.log(err || data); + }); + } + syncActiveFile() { let tableBarItem = JSON.parse(this.store.get("persist:tableBarItem")); if (!tableBarItem) { @@ -21,38 +61,48 @@ class UploadUtils { let activeFile = tableBarItem.activeKey?tableBarItem.activeKey.replaceAll('"',""):undefined; console.log("activeFile:", activeFile) if (activeFile) { - cos.headObject({ - Bucket:Bucket, - Region:Region, + let dirMessage = JSON.parse(this.store.get("persist:dirMessage")); + UploadUtils.cos.headObject({ + Bucket:UploadUtils.Bucket, + Region:UploadUtils.Region, // 不能以 / 开头 Key: activeFile, }, function(err, data) { - console.log("err || data.CommonPrefixes",err || data); + console.log("err || data.CommonPrefixes"+activeFile,err || data); if (data&&data.ETag){ // 文件存在,比较MD5值 - let md5= data.ETag.replaceAll('"',"") - console.log("fileList[0].ETag",md5) - }else { - console.log("cos.uploadFile") - cos.uploadFile({ - Bucket: Bucket, /* 填入您自己的存储桶,必须字段 */ - Region: Region, /* 存储桶所在地域,例如 ap-beijing,必须字段 */ - Key: activeFile, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */ - FilePath: activeFile, /* 必须 */ - SliceSize: 1024 * 1024 * 5, /* 触发分块上传的阈值,超过5MB使用分块上传,非必须 */ - onTaskReady: function (taskId) { /* 非必须 */ - console.log(taskId); - }, - onProgress: function (progressData) { /* 非必须 */ - console.log(JSON.stringify(progressData)); - }, - onFileFinish: function (err, data, options) { /* 非必须 */ - console.log(options.Key + '上传' + (err ? '失败' : '完成')); - }, - // 支持自定义headers 非必须 - }, function (err, data) { - console.log(err || data); + let onlineMd5= data.ETag.replaceAll('"',"") + let fileMd5 + if (dirMessage&&dirMessage.data&&dirMessage.data.activeFile) { + fileMd5= dirMessage.data.activeFile.fileMd5; + } + if (!fileMd5){ + fileMd5=md5(readFileSync(activeFile).toString()) + } + if (onlineMd5===fileMd5){ + return + } + console.log("fileList[0].ETag",onlineMd5) + let number = dialog.showMessageBoxSync({ + "message":"云文件已修改是否同步到本地", + "type":"info", + "buttons":["是","否"], + "defaultId":0 }); + if (number===0){ + UploadUtils.selfDownLoadFile(activeFile) + }else if(number===1){ + if (dialog.showMessageBoxSync({ + "message":"是否使用本地文件覆盖远程文件", + "type":"info", + "buttons":["是","否"], + "defaultId":0 + })===0){ + UploadUtils.selfUploadFile(activeFile) + } + } + }else { + UploadUtils.selfUploadFile(activeFile) } }); } diff --git a/src/pages/Note/Hlexical/plugins/SaveFilePlugin.js b/src/pages/Note/Hlexical/plugins/SaveFilePlugin.js index dad4e79..4c32741 100644 --- a/src/pages/Note/Hlexical/plugins/SaveFilePlugin.js +++ b/src/pages/Note/Hlexical/plugins/SaveFilePlugin.js @@ -11,7 +11,7 @@ import md5 from "md5" import {message} from "antd"; const {ipcRenderer} = window.require('electron') import "./ToobarPlugin.less" -import {newFileAdd} from "../../../../redux/dirMessage_reducer"; +import {newFileAdd, updateFileMd5} from "../../../../redux/dirMessage_reducer"; const SaveFilePlugin=(props)=> { let activeKey = useSelector(state => state.tableBarItem.activeKey); const dispatch = useDispatch(); @@ -71,7 +71,8 @@ const SaveFilePlugin=(props)=> { // 修改当前文件名 dispatch(updatedSavedFile({filePath: filePath})) // 文件目录更新 - dispatch(newFileAdd({fileName:getFileFullNameByPath(filePath),dirFlag:false,children:[],filePath: filePath})) + dispatch(newFileAdd({fileName:getFileFullNameByPath(filePath),dirFlag:false,children:[],filePath: filePath, + fileId:filePath,fileMd5:md5(resultSave)})) } }) return @@ -82,20 +83,27 @@ const SaveFilePlugin=(props)=> { const editorStateSave = {"editorState": JSON.parse(editorState)}; resultSave = JSON.stringify(editorStateSave); } + + // 后期不再读取文件,直接读取文件MD5 importFile(filePath).then(value => { let save + let newFileMd5 if (props.filePath.endsWith(".md")){ // editorState - save = (isEmpty(value)) || md5(resultSave) !== md5(value.toString()); + newFileMd5 = md5(value.toString()); + save = (isEmpty(value)) || md5(resultSave) !== newFileMd5; }else { - save = (isEmpty(value)) || md5(resultSave) !== md5(JSON.stringify(JSON.parse(value.toString()))); + newFileMd5 = md5(JSON.stringify(JSON.parse(value.toString()))); + save = (isEmpty(value)) || md5(resultSave) !== newFileMd5; } - if (save) { - overWriteFile(filePath, resultSave) - console.log("保存成功"+ filePath) - // messageApi.open({type:"success",content:"保存成功:" + filePath,duration:1}) - message.success("保存成功:" + filePath) + overWriteFile(filePath, resultSave).then(()=>{ + console.log("保存成功"+ filePath) + // 修改文件Md5 + dispatch(updateFileMd5({filePath,"fileMd5":newFileMd5})) + // messageApi.open({type:"success",content:"保存成功:" + filePath,duration:1}) + message.success("保存成功:" + filePath) + }) } }).catch(error => console.error(error) @@ -115,10 +123,5 @@ const SaveFilePlugin=(props)=> { }; },[editor,onChange] ) - // return ( - //
- // {contextHolder} - //
- // ) } export default SaveFilePlugin diff --git a/src/redux/dirMessage_reducer.js b/src/redux/dirMessage_reducer.js index 34d733b..068a85a 100644 --- a/src/redux/dirMessage_reducer.js +++ b/src/redux/dirMessage_reducer.js @@ -1,6 +1,12 @@ import {createSlice} from '@reduxjs/toolkit' import {isEmpty} from "../utils/ObjectUtils"; -import {fileDirFormat, filePathSplit, getFileDirByPath, getFileFullNameByPath} from "../utils/PathOperate"; +import { + fileDirFormat, + filePathSplit, + getFileDirByPath, + getFileFullNameByPath, + getFileNameByPath +} from "../utils/PathOperate"; import {FileTree, insertNode, removeNode, updateNode} from "../utils/FileTree" import {func} from "prop-types"; /* @@ -19,8 +25,9 @@ import {func} from "prop-types"; export const dirMessageSlice = createSlice({ name: 'dirMessage', initialState: { - data: [], - dirTree:new FileTree("root","/root",true,[]) + // 暂时只存储文件信息,不存贮文件夹信息。 + data: new Map([['root','']]), + dirTree:new FileTree("root","/root",true,[],"root",undefined) }, reducers: { dirAdd: (state, action) => { @@ -31,6 +38,7 @@ export const dirMessageSlice = createSlice({ newFileAdd :(state,action)=>{ console.log("dirMessage:newFileAdd", state, action) insertNode(state.dirTree,action.payload) + state.data.set(action.payload.fileId?action.payload.fileId:action.payload.filePath,action.payload) }, dirRemove:(state,action)=>{ console.log("dirMessage:dirRemove", state, action) @@ -60,6 +68,41 @@ export const dirMessageSlice = createSlice({ {"fileName":newFileName,"filePath":newFilePath,"dirFlag":false,"children":[]} ) }, + updateFileMd5:(state,action)=>{ + console.log("dirMessage:updateFileMd5", state, action) + updateNode(state.dirTree, + {"filePath":action.payload.filePath}, + {"fileMd5":action.payload.fileMd5}) + console.log("state.data",...state.data) + let fileId = action.payload.fileId?action.payload.fileId:action.payload.filePath; + if (state.data.fileIdKey){ + state.data.fileIdKey.fileMd5=action.payload.fileMd5 + }else { + let newMap = new Map(); + state.data.forEach((value, key) => { + newMap.set(key, value); + }); + newMap.set(action.payload.filePath,{ + fileTitle:getFileNameByPath(action.payload.filePath), + fileName:getFileFullNameByPath(action.payload.filePath), + fileDir:false, + children:[], + filePath:action.payload.filePath, + fileId:fileId, + fileMd5:action.payload.fileMd5 + }) + state.data=newMap + } + state.data.set(action.payload.filePath,{ + fileTitle:getFileNameByPath(action.payload.filePath), + fileName:getFileFullNameByPath(action.payload.filePath), + fileDir:false, + children:[], + filePath:action.payload.filePath, + fileId:fileId, + fileMd5:action.payload.fileMd5 + }) + }, dirFileAdd:(state,action)=>{ console.log("dirMessage:dirFileAdd", state, action) let fileDir = action.payload.fileDir @@ -72,6 +115,7 @@ export const dirMessageSlice = createSlice({ "children": [] } insertNode(state.dirTree,fileMessage) + state.data.set(action.payload.fileId?action.payload.fileId:action.payload.filePath,fileMessage) }, dirDirAdd:(state,action)=>{ console.log("dirMessage:dirDirAdd", state, action) @@ -101,6 +145,7 @@ export const { nextDirAdd, dirRemove, updateFileName, + updateFileMd5, dirFileAdd, dirDirAdd, dirFileRemove, diff --git a/src/utils/FileTree.js b/src/utils/FileTree.js index 7ffdae5..5437c66 100644 --- a/src/utils/FileTree.js +++ b/src/utils/FileTree.js @@ -3,11 +3,13 @@ import {isObject} from "formik"; import {isEmpty} from "./ObjectUtils"; export class FileTree { - constructor(fileName,filePath,dirFlag,children) { + constructor(fileName,filePath,dirFlag,children,fileId,fileMd5) { this.fileName = fileName; this.filePath = filePath; this.dirFlag = dirFlag; this.children = children; + this.fileId = fileId; + this.fileMd5= fileMd5; this.pass=0 this.end=0 } @@ -31,7 +33,8 @@ export function insertNode(root,fileMessage) { currentNode=find; currentNode.pass=currentNode.pass+1 }else { - let newNode = new FileTree(fileDirSplit[i],fileDirFormatArray(fileDirSplit.slice(0,i+1)),true,[]); + let newNode = new FileTree(fileDirSplit[i],fileDirFormatArray(fileDirSplit.slice(0,i+1)),true,[], + fileDirFormatArray(fileDirSplit.slice(0,i+1)),undefined); currentNode.children.push(newNode) currentNode=newNode currentNode.pass=currentNode.pass+1 @@ -88,10 +91,27 @@ export function updateNode(root,fileMessage,newFileMessage) { if (i===fileDirSplit.length-1) { let node =currentNode.children.find(file=> file.fileName===fileMessage.fileName); - node.fileName=newFileMessage.fileName - node.filePath=newFileMessage.filePath - node.dirFlag=newFileMessage.dirFlag - node.children=newFileMessage.children + if (!node){ + break + } + if (newFileMessage.fileName){ + node.fileName=newFileMessage.fileName + } + if (newFileMessage.filePath){ + node.filePath=newFileMessage.filePath + } + if (newFileMessage.dirFlag){ + node.dirFlag=newFileMessage.dirFlag + } + if (newFileMessage.children){ + node.children=newFileMessage.children + } + if (newFileMessage.fileId){ + node.fileId = newFileMessage.fileId + } + if (newFileMessage.fileMd5){ + node.fileMd5 = newFileMessage.fileMd5 + } } } }