zoukankan      html  css  js  c++  java
  • 06.《Electron 跨平台开发实战》- chapter06-操作文件系统

    项目代码

    https://github.com/electron-in-action/firesale/tree/chapter-6

    代码解析

    窗口标题

    • 根据文件路径获取文件名: path.basename(filePath)
    • 设置窗口标题:currentWindow.setTitle(title)
    const path = require('path');
    ...
    const updateUserInterface = () => {
        let title = "Fire Sale";
        if (filePath) {
            title = `${path.basename(filePath)} - ${title}`; //path.basename:根据文件路径获取文件名
            currentWindow.setTitle(title);
        }
    };
    
    

    检测文件是否被修改过

    //文本框监听`keyup`,
    markdownView.addEventListener('keyup', (event)=>{
        const currentContent = event.target.value;
        renderMarkdownToHtml(currentContent);
        updateUserInterface(currentContent != originalContent);
    });
    
    const updateUserInterface = (isEdited) => {
        let title = "Fire Sale";
        if (filePath) {
            title = `${path.basename(filePath)} - ${title}`; //path.basename:根据文件路径获取文件名
            //WindowLinux
            if (isEdited) {
                title = `${title} (edited)`;
            }
    
            currentWindow.setTitle(title);
    
            //MacOS
            currentWindow.setDocumentEdited(isEdited);
        }
    
    };
    

    保存为html

    注意:electron 9.x 打开文本对话框的函数已经做了修改,与低版本写法已经不一样了

     dialog.showSaveDialog(win,{})
      .then(result=>{})
      .catch(err =>{
      });
    
    • main.js
    //保存为html
    const saveHtml = exports.saveHtml = (targetWindow, content) => {
        dialog.showSaveDialog(targetWindow, {
            title: 'Save Html',
            defaultPath: app.getPath('documents'), //默认使用操作系统中用户的documents目录
            filters: [
                { name: 'HTML Files', extensions: ['html', 'htm'] }
            ]
        }).then(result => {
    
            if (result.filePath) {
                try {
                    fs.writeFileSync(result.filePath, content);
                } catch (error) {
                    console.log("保存html发生异常:" + error)
                }
            }
        }).catch(err => {
            console.log(err)
        })
    };
    
    • renderer.js
    //保存为Html
    saveHtmlButton.addEventListener('click', () => {
        mainProcess.saveHtml(currentWindow, htmlView.innerHTML);
    });
    
    

    保存当前文件

    • main.js
    
    //保存Markdown
    const saveMarkdown = exports.saveMarkdown = (targetWindow, file, content) => {
    
        if (file) { //如果文件为空,打开文件保存对话框
            fs.writeFileSync(file, content);
        } else { //如果文件为空,打开文件保存对话框
            dialog.showSaveDialog(targetWindow, {
                title: 'Save Html',
                defaultPath: app.getPath('documents'), //默认使用操作系统中用户的documents目录
                filters: [
                    { name: 'HTML Files', extensions: ['html', 'htm'] }
                ]
            }).then(result => {
                if (result.filePath) {
                    fs.writeFileSync(result.filePath, content);
                }
            }).catch(err => {
                console.log(err)
            })
        }
    };
    
    • renderer.js
    //保存Markdow
    saveMarkdownButton.addEventListener('click', () => {
        mainProcess.saveMarkdown(currentWindow, filePath, markdownView.value);
    });
    
    

    回滚文件

    • renderer.js
    //回撤
    revertButton.addEventListener('click', () => {
        markdownView.value = originalContent;
        renderMarkdownToHtml(originalContent);
    });
    

    通过拖拽打开文件

    拖拽事件:document.addEventListener('dragStart/drag/dragleave/drop', event => {event.preventDefault(); ...});

    拖拽文件信息:

    event.dataTransfer.items[0] //dragover

    event.dataTransfer.files[0] //drop

    书中给的示例,与实际有出入:

    • makedown文件获取的 file.tpye='';
    
    //拖拽到上边不松手这时,只能)读取文件元信息
    const getDraggedFile = (event) => {
        return event.dataTransfer.items[0];
    };
    ...
    const file = getDroppedFile(event); //file.tpye='';
    
    • fileTypeIsSupported() 对 makedown文件 判定有误,改用fileTypeIsSupportedByExtName()
    // 是否支持文件类型
    const fileTypeIsSupported = (file) => {
        return ['text/plain', 'text/markdown'].includes(file.type);
    }
    
    
    
    //拖拽到上边
    document.addEventListener('dragover', event => {
        event.preventDefault();
        const file = getDraggedFile(event); //有问题:file.type=''
        /*
            if (fileTypeIsSupported(file)) {
                markdownView.classList.add('drag-over'); //支持文件类型样式
            } else {
                markdownView.classList.add('drag-error');//不支持文件类型样式
            }
        */
    });
    //离开
    document.addEventListener('dragleave', event => {
        event.preventDefault();
        markdownView.classList.remove('drag-over');
        markdownView.classList.remove('drag-error');
    });
    //拖拽到上边并松手
    document.addEventListener('drop', event => {
        event.preventDefault();//禁用默认事件
        const file = getDroppedFile(event);
        let extName = path.extname(file.path);
        if (fileTypeIsSupportedByExtName(extName)) { //if (fileTypeIsSupported(file)) {
            mainProcess.openFile(currentWindow, file.path); //支持文件类型样式
        } else {
            alert('that file type is not supported');
        }
        markdownView.classList.remove('drag-over');
        markdownView.classList.remove('drag-error');
    });
    
    //拖拽到上边不松手这时,只能)读取文件元信息
    const getDraggedFile = (event) => {
        return event.dataTransfer.items[0];
    };
    //(拖拽到上边并松手才能)读取文件File对象
    const getDroppedFile = (event) => {
        return event.dataTransfer.files[0];
    };
    
    // 是否支持文件类型
    const fileTypeIsSupported = (file) => {
        return ['text/plain', 'text/markdown'].includes(file.type);
    }
    
    // 是否支持文件类型
    const fileTypeIsSupportedByExtName = (extName) => {
        return ['.md', '.markdown'].includes(extName);
    }
    

    监控文件变化

    监控文件 Node.js : const watcher = fs.watch(file, (event)=>{ if(event==='change') {...}})

      const watcher = fs.watch(file, (event) => { //Node 的文件监控器,文件有变化,重新读取文件
            console.log(event);
            if (event === `change`) {
                const content = fs.readFileSync(file).toString();;
                targetWindow.webContents.send('file-opened', file, content);
            }
        });
    
        openFiles.set(targetWindow, watcher);
    

    停止监控文件:watcher.close()

    //停止监控文件:
    const stopWatchingFile = (targetWindow) => {
        if (openFiles.has(targetWindow)) {
            openFiles.get(targetWindow).close(); //.stop() 已经不存在
            openFiles.delete(targetWindow);
        }
    };
    

    提示未保存修改

    • main.js
    //监控文件
    const startWatchingFile = (targetWindow, file) => {
        stopWatchingFile(targetWindow);
    
        //问题:fs.watch ()在windows系统:一次修改会触发2次'change'
        const watcher = fs.watch(file, (event) => { //Node 的文件监控器,文件有变化,重新读取文件
            if (event === `change`) {
                console.log('watch-change');
                const content = fs.readFileSync(file).toString();;
                targetWindow.fileContentChanged = true;
                targetWindow.webContents.send('file-changed', file, content); //文档内容发生改变消息
            } else {
                targetWindow.fileContentChanged = false;
            }
        });
    
    
        openFiles.set(targetWindow, watcher);
    };
    
    
    //停止监控文件
    const stopWatchingFile = (targetWindow) => {
        if (openFiles.has(targetWindow)) {
            let watcher = openFiles.get(targetWindow);
            //https://www.nodeapp.cn/fs.html#fs_watcher_close
            watcher.close();//watcher.stop();
            openFiles.delete(targetWindow);
        }
    };
    
    
    • renderer.js
    //打开文件
    ipcRenderer.on('file-opened', (event, file, content) => {
        if (currentWindow.isDocumentEdited()  //macOS系统
            || currentWindow.fileContentChanged) {//其它平台
            event.preventDefault(); //阻止窗口关闭
            dialog.showMessageBox(currentWindow, {
                type: 'warning',
                title: '当前文档尚未保存',
                message: "当前文档尚未保存,新打开的文档将覆盖当前文档",
                buttons: ['继续打开', '取消']
            }).then(result => {
                if (result.response === 0) {
                    renderFile(file, content);
                }
            });
        } else {
            renderFile(file, content);
        }
    });
    
    //文件内容发生改变,提示用户
    ipcRenderer.on('file-changed', (event, file, content) => {
    
        remote.dialog.showMessageBox(currentWindow, {
            type: 'warning',
            title: '警告',
            message: "文档内容已被其它程序修改,是否加载并覆盖当前的内容",
            buttons: ['加载覆盖', '取消']
        }).then(result => {
            if (result.response === 0) {
                renderFile(file, content);
            }
        });
    
    });
    
    const renderFile = (file, content) => {
        filePath = file;
        originalContent = content;
    
        markdownView.value = content;
        renderMarkdownToHtml(content);
    
        updateUserInterface(false);
    }
    
    

    知识点

    选择对话框:dialog.showMessageBox()

    
        remote.dialog.showMessageBox(currentWindow, {
            type: 'warning',
            title: '警告',
            message: "文档内容已被其它程序修改,是否加载并覆盖当前的内容",
            buttons: ['加载覆盖', '取消']
        }).then(result => {
            if (result.response === 0) {
                renderFile(file, content);
            }
        });
    
    

    监控文件变化

    fs.watch()fs.watchFile()

    fs.watch()文档: https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_fs_watchfile_filename_options_listener

    • 问题:
      fs.watch ()在windows系统:一次修改会触发2次'change'
    关闭监听文件变化 watcher.close()
     const watcher = fs.watch(...);
     watcher.close(); 
    
  • 相关阅读:
    自定义promise的实现
    数据双向邦定1
    上线遇到的bug
    UEGrids.js
    staticFileServer.js
    Promise
    响应式布局实例
    悬浮框的兼容性
    Fiddler Web Debugger
    js根据当前日期提前N天或推后N天的方法
  • 原文地址:https://www.cnblogs.com/easy5weikai/p/13099342.html
Copyright © 2011-2022 走看看