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:根据文件路径获取文件名
        //Window\Linux
        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(); 
posted @ 2020-06-12 14:46  easy5  阅读(412)  评论(0编辑  收藏  举报