Typora是一款非常优秀的markdown编辑器,他自带的图片上传服务以及其他各种功能方便了很多。但是因为使用picgo实现的图片上传服务依赖于网络,哪怕是本地图床转储,这不利于离线环境的跨设备传输。有时候我们要给别人拷贝一份md文件,却因为对方链接不到自己的图床而苦恼,于是我尝试了在md文件中用data协议嵌入图片数据的方式,尝试后发现可以成功。
结合utools的图片压缩插件和编码解码插件,使用了一段时间,但是每次都有截图,复制到utools压缩,编码,还是有些麻烦。后来想着魔改一个picgo的插件,让它直接返回data协议格式的数据。
魔改picgo的插件web-uploader
web-uploader是picgo-core的上传插件,主要用来支持picgo不支持的一些OSS服务。
首先在Typora中安装picgo-core,具体不在说,网上都有教程。另外,picgo-core插件运行依赖于npm,所以还得装node.js,并添加至环境变量。这不是本文 的重点。
然后进入picgo程序的目录(因为没有添加到环境变量中,所以得去其目录下面运行)%APPDATA%/Typora/picgo/win64
。在这里打开控制台窗口,输入
.\picgo install web-uploader
这样picgo就会安装插件web-uploader,插件安装的目录在%USERPROFILE%/.picgo/node_modules
,该插件的主要功能代码在picgo-plugin-web-uploader\src\index.js
中。我们对其进行修改,其代码原来的功能大概就是读取配置文件的信息,获取图片数据,构造POST的数据包,发往OSS服务,获取响应包,读取返回的图片链接,写入picgo提供的接口中,我们去掉数据发送,改成直接返回base64数据即可
module.exports = (ctx) => {
const register = () => {
ctx.helper.uploader.register('web-uploader', {
handle,name: '自定义Web图床',})}const handle = async function (ctx) {
try {
let imgList = ctx.outputfor (let i in imgList) {
let image = imgList[i].base64Imageif (!image && imgList[i].buffer) {
image = imgList[i].buffer.toString('base64')}let body = await ((ext,base64)=>{
return "data://image/"+ext.substr(1)+";base64,"+base64})(imgList[i].extname,image)delete imgList[i].base64Imagedelete imgList[i].bufferif(body){
imgList[i]['imgUrl'] =body}else{
ctx.emit('notification', {
title: '数据异常',//JSON解析失败body: '请检查'})}}} catch (err) {
ctx.emit('notification', {
title: '上传失败',body: JSON.stringify(err)})}}return {
uploader: 'web-uploader',// transformer: 'web-uploader',// config: config,register}
}
修改配置文件
picgo默认使用的上传服务是其自带的sms上传服务,所以需要修改配置文件,这个配置文件可以在Typora里面打开,也可以在%USERPROFILE%/.picgo/config.json
中直接打开。修改如下,这里展示的是只有web-uploader的情况,我们的代码里面没有读取配置,所以web-uploader的配置项空着,picgoBed["uploader"]="web-uploader"
声明使用web-uploader,这里会默认有一个配置项叫picgoBed["current"]="sms"
,删掉就好
{
"picgoPlugins": {
"picgo-plugin-web-uploader": true},"picBed": {
"uploader": "web-uploader","web-uploader": {
}}
}
修改Typora的处理代码
经过上面的修改,图片上传后picgo就会返回图片的base64数据
但是直接在Typora中上传会报错fail to parse result image path from:
后面跟的是picgo的运行结果。根据Typora的官方文档,它是只支持https和http协议的返回结果的。我们打开开发者工具,发现picgo的结果会被一个叫frame.js
文件输出,我们在该文件(最新版的Typora中位于resources/appsrc/window/frame.js
)中全局搜索该错误信息,定位到以下代码
)).then(t=>{
var i = "picgo-app" == n ? t : N(t, n);return i.length == e.length ? Promise.resolve(i) : (console.log(t),Promise.reject("fail to parse result image path from: " + t))})
可以看到,执行正确则使用Promise.resolve(i)
解析结果,否则就报错。而结果i
来自与N(t,n)
函数的执行。t
在这里是picgo的执行输出,就上图的内容,n
是使用的命令行cmdline,我们定位到N
函数
function N(e, t) {
//e是picgo的结果,t是使用的命令return "picgo-app" == t ? function(e) {
try {
var t = JSON.parse(e);return t.success ? t.result : []} catch (e) {
return []}}(e) : function(e) {
for (var t = e.split(/\r?\n/), n = [], i = t.length - 1; i >= 0; i--) {
var r = t[i].trim();if (r)if (/^(https?|ftp|file):\//.exec(r))//重点在这里,这里对结果每行进行判断,如果是`http|https|file|ftp`协议就将该行写入数组,这里没有data协议,所以会报错,我们在这里加上data协议。正则表达式改成// ^(https?|ftp|file|data):\///这里匹配的是data:/,所以我们在picgo的插件中加上的是`data://`,而不是只有`data:`n.unshift(r);else if (/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}\/.*$/.exec(r))n.unshift("http://" + r);else if (n.length)break}return n}(e)}
修改后保存。
大功告成
修改后保存。
大功告成