使用 markdown,最方便的莫过于利用图床功能,将图片保存到云端,然后转换成 ![]()
结构。避免担心图片存在某一台电脑设备上,这样就可以把文章内容复制到任何一个地方和平台,继续无缝的写作下去。
今天,我们就来说一说怎么在自建 markdown 编辑器上,增加图床功能。
我们的主角是:「PicGo」。
安装 PicGo
npm install picgo --save
配置 PicGo
picgo 的默认配置文件为~/.picgo/config.json。其中~ 为用户目录。不同系统的用户目录不太一样。
linux 和 macOS 均为 ~/.picgo/config.json。
windows 则为 C:Users 你的用户名.picgoconfig.json
当然也可以根据自己的需要,将配置文件放在不同路径下,主要参考 picgo 官网描述来配置即可,下面是是 upyun 配置内容。
{
"picBed": {
"current": "upyun",
"uploader": "upyun",
"upyun": {
"bucket": "coding", // 存储空间名,及你的服务名
"operator": "coding01", // 操作员
"password": "**********", // 密码
"options": "", // 针对图片的一些后缀处理参数"path":"blog/", // 自定义存储路径,比如 img/"url":"http://***.****.com"
}
},
"picgoPlugins": {}}
如果没配置,则可以使用默认的 sm.ms 图床。
触发上传图床
在写作过程中,触发上传比较简单的办法,我觉得有两种方法比较便捷,一种是直接复制粘贴图片,另一种就是拖拽图片到编辑器。
今天重点来说说怎么直接复制粘贴上传图片。
剪切板
首先,必须要借助 electron 的剪切板读取复制的图片信息,clipboard 操作剪切板功能也很简单,直接看代码:
const {clipboard} = require('electron')
// 获取剪切板图片,type String (optional) - Can be selection or clipboard. selection is only available on Linux.
// 返回 NativeImage- 剪贴板中的图像内容。clipboard.readImage([type])
具体参考 electron clipboard https://electronjs.org/docs/api/clipboard
粘贴
我们只需要「Ctrl + Shift + V」粘贴操作,编辑器会判断,如果剪切板上是图片时,我们就执行上传操作。
全局快捷键
我们先用 electron 提供的「全局快捷键」看看效果。
// 注册一个 'CommandOrControl+X' 的全局快捷键
const ret = globalShortcut.register('CommandOrControl+v', () => {console.log('CommandOrControl+v is pressed')
// 上传剪贴板里的第一张图片(上传时会将格式转为 png)let callback = picgo.upload()
console.log(callback)
})
if (!ret) {console.log('registration failed')
}
// 检查快捷键是否注册成功
console.log(globalShortcut.isRegistered('CommandOrControl+v'))
看看打印出来的 log,这里也能看出默认上传图床为 SM.MS:
除了使用「全局快捷键」,我们还可以使用「本地快捷键」方式:
本地快捷键
使用「本地快捷键」之前,需要创建菜单和菜单项,通过菜单项绑定对应的快捷键。
mainMenu(mainWindow);
// 设置快捷键和对应的动作
{
label: '上传图片',
accelerator: process.platform === 'darwin' ? 'Command+Shift+V' : 'Ctrl+Shift+V',
click: () => {mainWindow.webContents.send('uploadImage');
}
},
通过 mainWindow.webContents.send
将快捷键动作推送给 ipcRenderer,接着可以在 renderer 中创建监听事件:
ipcRenderer.on('uploadImage', (event) => {console.log('messageBus CommandOrControl+v is pressed')
ipcRenderer.send('uploadImage');
})
获取剪切板的图片推荐放到 ipcMain 上去做,而且上传图片也是耗时事件。所以在 ipcMain 创建 uploadImage 事件:
ipcMain.on('uploadImage', function() {let img = clipboard.readImage()
if (!img.isEmpty()) {let callback = picgo.upload()
console.log(callback)
}
})
好了,到此为止,我们执行,通过快捷键「Command+Shift+V」看看打印 log:
除了以上两种方式外,我们还有第三种使用快捷键的方法,就是利用 ace 编辑器自带的 Command 功能
ace Command
在 EditorPage 的 editorInit(editor)
初始化方法中增加一个 Command,并绑定对应的快捷键:
editor.commands.addCommand({
name: '上传图片',
bindKey: {win: 'Ctrl-Shift-M', mac: 'Command-Shift-M'},
exec: function(editor) {console.log('上传图片');
messageBus.uploadImage();},
readOnly: true // false if this command should not apply in readOnly mode
});
同样的,创建 messageBus.uploadImage()
uploadImage() {console.log('messageBus ACE Command is pressed')
ipcRenderer.send('uploadImage');
}
综上,个人偏向使用 ace Command 方法,把上传图片的功能局限到编辑器范畴内,也符合功能的使用。
到此,我们初步做到将图片上传到默认的 sm.ms 上了,接下来就是显示的问题了。
上传图床后显示
首先看看 picgo 提供给我们几个和上传有关的状态函数:
// 图床上传进度
picgo.on('uploadProgress', progress => {console.log('uploadProgress')
console.log(progress)
})
picgo.on('beforeTransform', ctx => {console.log('beforeTransform')
console.log(ctx.input) // ['/xxx/xxx.jpg']
})
picgo.on('beforeUpload', ctx => {console.log('beforeUpload')
console.log(ctx.output) // [{base64Image, fileName, width, height, extname}]
})
picgo.on('afterUpload', ctx => {console.log('afterUpload')
console.log(ctx.output) // [{fileName, width, height, extname, imgUrl}] <- 注意有 imgUrl 了。})
picgo.on('finished', ctx => {console.log('finished')
console.log(ctx.output) // [{fileName, width, height, extname, imgUrl}] <- 注意有 imgUrl 了。})
picgo.on('failed', error => {console.log('failed')
console.log(error) // 错误信息
})
picgo.on('notification', notice => {console.log('notification')
console.log(notice) // {title, body, text?}
})
其中,我们需要用到的有:uploadProgress
、afterUpload
、failed
、notification
。
如果上传错误的话,可以通过多方面来判断:1. 可以利用 progress = -1;2. error;3. notification 也会输出错误提示和内容,这里推荐直接使用 notification。
我们先看 afterUpload
返回的数据,需要将 imgUrl
内容填充给编辑器。
picgo.on('afterUpload', ctx => {console.log('afterUpload')
console.log(ctx.output.imgUrl) // [{fileName, width, height, extname, imgUrl}] <- 注意有 imgUrl 了。// 将 imgUrl 内容发给编辑器。mainWindow.webContents.send('imgUrl', ctx.output[0].imgUrl);
})
接收端直接接收该事件,并 emit 给 vue 页面:
// renderer/main.js
ipcRenderer.on('imgUrl', (event, imgUrl) => {messageBus.$emit('imgUrl', imgUrl);
})
// EditorPage.vue
messageBus.$on('imgUrl', (imgUrl) => {console.log(`imgUrl ${imgUrl}`);
// 格式化为 markdown 格式,insert 到编辑器中
this.editor.editor.insert(`![](${imgUrl})`);
});
直接看结果
总结
接下来还有很多东西需要做,如:
- 增加各个图床 (七牛、又拍云等) 的配置设置;
- 将上传图床的功能封装;
- 增加拖拽图片上传图床功能;
- 增加图片上传进度和成功与否的提示等。
当然,我们的目标是在写出满足自己写作需要的 markdown 编辑器的同时,也学习如何使用 Electron 框架。
学会了
- electron 菜单项 https://electronjs.org/docs/api/menu-item
- electron「本地快捷键」和「全局快捷键」https://electronjs.org/docs/tutorial/keyboard-shortcuts
- electron 剪贴板 https://electronjs.org/docs/api/clipboard
「Markdown 编辑器」系列
- 利用 Electron 简单撸一个 Markdown 编辑器 (1) https://mp.weixin.qq.com/s/lXESP6Sa03ytCwppDwWkdg