Typora自定义命令上传图片

本文最后更新于:2023年2月3日 下午

本文介绍如何使用Typora的自定义命令上传图片,将文章图片上传至私有云服务器的git图床,并返回图片链接的方法。

前言

Typora[1]是我常用的一个可跨多平台使用的Markdown[2]编辑器。在Typora 0.9.84[3]版本更新后,增加了对uPic (macOS)、PicGo-Core、PicGo.app(仅限中文)和自定义命令上传图像的支持。这个功能,能够让用户自动将图片上传至图床,并返回图片链接。

Typora只是一个文本编辑器,其本身没有存储图片的功能。插入到Typora文章里的图片,都是只记录图片在本地目录中的相对路径,或图片的网络链接。但一旦图片在本地的相对位置或文件名发生变动、修改,图片就不能正常显示了。并且逐章逐字地更新图片的引用,非常的费时费力。

因此大家习惯性地将本地图片,上传至网络上的图床,依据一定的规则生成永久的图片链接进行引用。这样,如果要更改图片,只要保持永久链接不变,只需要在网络图片上对图片进行更新就好了,不需要修改已发布的文章内容。

在Typora 0.9.84版本后,我们可以使用自定义命令,在全平台实现图片自动上传图床的功能。让我们来看看如何实现。

Typora

动手实现

在Typora插入一张图片,Typora会调用我们预先设置好的自定义命令,传入要上传图片的路径参数,通常自定义命令是调用我们写好的一个上传图片的脚本。脚本执行完后,按照Typora给定的输出格式,输出与传入图片路径参数一一对应的URL。Typora会依次读取URL,把本地图片的引用,依次替换为脚本输出的URL。

因为我是使用Git服务在云服务器上部署的图床,因此在本地是通过git命令上传图片至远端的仓库来实现更新图床。

综上,我们所需要做的是这四步:

  • 配置好脚本的运行环境。
  • 编辑自定义脚本(可以是Shell、Go、Python等)。
  • 在Typora偏好设置中设置好自定义脚本的调用命令。
  • 通过Typora的图片上传测试。

配置脚本运行环境

Windows

在Windows系统上,推荐使用的是Python脚本。

因为在Windows上调用Shell脚本的效果,有些不太符合预期。本章使用的是Python3.9。

Python环境的配置过程,这里不多赘述。请自行Google或Bing。

MacOS

在MacOS上,我使用的是Shell脚本,无需额外的配置。

Linux

在Linux上,使用Shell脚本,也无需额外配置。

Typora传参、取结果机制

对于Typora的传参,这里需要注意的有几点:

  • Typora通过命令行传递的参数是一组图片的本地路径的字符串,所传图片的数量不固定,路径由引号包裹,每个路径间,由空格隔开。格式类似于:

    1
    2
    3
    4
    5
    #Windows
    <Custom Command> "C:\\Users\\<YOUR_USER_NAME>\\AppData\\Local\\Temp/typora-icon2.png" "C:\\Users\\<YOUR_USER_NAME>\\AppData\\Local\\Temp/typora-icon.png"

    #MacOS
    <Custom Command> "/<YOUR_USER_NAME>/Application/Typora/typora-icon2.png" "/<YOUR_USER_NAME>/Application/Typora/typora-icon.png"
  • Typora传递的图片路径参数是在自定义命令的尾部传递。因此如果你的脚本需要在命令行接收额外的自定义的参数,应该在这些路径参数之前。并且在脚本中自行进行处理。

对于Typora取结果的机制,要注意的有几点:

  • Typora输出结果的格式如下:

    1
    2
    3
    Upload Success:
    https://sample.com/1.jpg
    https://sample.com/2.jpg

    从字符串Upload Success:开始,每一行对应一个图片的URL。只取Upload Success:和之后的n行,n为由Typora传递图片路径参数的数量。n行之后的信息,Typora会进行忽略。

  • 实际上,Typora并不关心脚本执行的过程,只要能接收的结果符合它所规定的格式,Typora即判定上传图片成功。并依次对图片引用的路径,替换为输出结果的URL。

因此我们可以写以下测试代码,假装进行了图片上传,但实际上只是简单粗暴地返回拼接后的URL地址:

Python(Windows)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# author: cnctema
# date: 2022-08-24 14:18
# use: python ./uploadImgGit.py <file_path_1> <file_path_2>

import os
import sys
# 各类配置信息
remote_path="default/" # 上传默认的路径
base_url="https://xxx.xxx.xx/" # 对象存储绑定的域名

print('Upload Success:')
paths = sys.argv
for path in paths:
name = os.path.basename(path)
print(base_url+remote_path+name) # 文件复制成功

Shell(MacOS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  # !/bin/bash
# -*- coding: UTF-8 -*-
# author: cnctema
# date: 2022-08-24 14:18
# use: /uploadImgGit.sh <file_path_1> <file_path_2>

# 各类配置信息
remote_path="default/" # 上传默认的路径
base_url="https://xxx.xxx.xx/" # 对象存储绑定的域名

# 输出结果
echo "Upload Success:"
for file in "$@";
do
file_name=$(basename "$file")
echo "$base_url""$remote_path""$file_name"
done

我们将在此基础上修改我们的代码。

完善脚本

Python(Windows)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# author: cnctema
# date: 2022-08-24 14:18
# use: python ./uploadImgGit.py <ASSET_REPO_PATH> <TYPORA_MARDOWN_FILE_PATH> <file_path_1> <file_path_2>
import os
import sys
import shutil

# 各类配置信息
local_repo_path = sys.argv[1] # 图床git仓库的本地目录
md_file_path = sys.argv[2] # 当前Markdown文件名
remote_path="default/" # 上传默认的路径
base_url="https://xxx.xxx.com/" # 自建git图床的域名

print('Upload Success:')
s_arg_index = 2
md_file_name = "undefined"
if(md_file_path.endswith(".md")):
s_arg_index = 3
md_file_name = os.path.basename(md_file_path).removesuffix(".md") # md文章名
paths = sys.argv[s_arg_index:len(sys.argv)]
for path in paths:
name = os.path.basename(path) # 目标文件名
newFilePath = os.path.join(local_repo_path, remote_path, md_file_name)

target = os.path.join(newFilePath, name) # 复制到的文件地址
file_path = os.path.abspath(path) # 源文件绝对地址

# copy
if not os.path.exists(newFilePath):
os.makedirs(newFilePath)
copyResult = shutil.copyfile(file_path,target) # 文件复制结果

# url replace + /?%#&=
newFileUrl = base_url+remote_path+md_file_name+"/"+name
oldstr = ['%','+',' ','/','?','#','&','=']
newstr = ["%25","%2B","%20","%2\F","%3\F","%23","%26","%3D"]
for i in range(0, len(oldstr)-1):
newFilePath = newFilePath.replace(oldstr[i], newstr[i])
pass

# result
if len(copyResult)>0:
print(newFileUrl) # 文件复制成功
else:
print(newFileUrl+"&!UploadFailed")

Shell脚本(MacOS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/bin/bash
# -*- coding: UTF-8 -*-
# author: cnctema
# date: 2022-08-24 14:18
# use: /uploadImgGit.sh <ASSET_REPO_PATH> <TYPORA_FILE_PATH> <file_path_1> <file_path_2>

# 各类配置信息
local_repo_path=$1 # 图床git仓库的本地目录
md_file_path=$2 # 当前Markdown文件名
remote_path="default/" # 上传默认的路径
base_url="https://xxx.xxx.com/" # 对象存储绑定的域名

s_arg_index=0
md_file_name="undefined" # markdown文件名称
md_file_suffix=$(echo "${md_file_path##*.}")

if test $md_file_suffix = "md"; then
# 存在md文件
md_file_name=$(basename $md_file_path)
md_file_name=$(echo "${md_file_name%%.*}")
s_arg_index+=1
fi

# 输出结果
echo "Upload Success:"
index=0
for file in "$@"; do
if test $[index] -gt $[s_arg_index]; then
# 文件名
name=$(basename "$file")

# copy
newFilePath="$remote_path""$md_file_name""/"
mkdir -p "$local_repo_path""$newFilePath"
newFileUrl="$newFilePath""$name"
cp -f "$file" "$local_repo_path""$newFileUrl"

# url replace + /?%#&=
newFileUrl=${newFileUrl//"+"/"%2B"}
newFileUrl=${newFileUrl//" "/"%20"}
newFileUrl=${newFileUrl//"/"/"%2F"}
newFileUrl=${newFileUrl//"?"/"%3F"}
newFileUrl=${newFileUrl//"%"/"%25"}
newFileUrl=${newFileUrl//"#"/"%23"}
newFileUrl=${newFileUrl//"&"/"%26"}
newFileUrl=${newFileUrl//"="/"%3D"}

# result
echo "$base_url""$newFileUrl"
fi
index+=1
done

脚本解析

脚本相对来说还是比较简单的,分几个部分:

  • 脚本参数接收处理

    Typora传递的${filename}参数,可能为空,这会影响到后面准确续取图片文件的操作。

  • 配置信息。

    由用户自行修改脚本,填写图床服务器的域名、默认目录地址。

  • 上传图片。

    复制图片至预设的路径<ASSET_REPO_PATH>,即本地存放图片的git仓库目录下。

    这里示例代码仅展示了复制文件的部分,你可以手动将图片文件提交到远端的仓库。因为在文章编辑阶段,我们大概率不想文件立刻被自动地传到网络上。而是在文章书写完后,确认后,再手动对所有图片进行一次性上传。

    当然如果你想这么做,可以补充完善这块逻辑。只需要增加git的上传命令即可。例如shell脚本:

    1
    2
    3
    4
    5
    #!/bin/bash
    cd <ASSET_REPO_PATH>
    git add .
    git commit -m "upload Img"
    git push
  • url转义。

    由于文件中可能存在一些特殊字符,而图床在对外提供服务时,会对特殊字符进行url转义。因此,我们也需要做同样的工作,以保证图片可以通过url正确访问。另外,空格符号也会干扰markdown语法的正常解析。

  • 输出结果。

    根据Typora给的结果返回格式,输出结果。

修改偏好设置

Typora偏好设置-图像-上传服务设定-上传服务 的多选项中选择Custom Command

上传图片自定义命令

如果需要在使用Ctrl+CCtrl+V,插入图片到编辑器时,就自动执行上传图片的脚本。则可以在 Typora偏好设置-图像-插入图片时的多选项中选择上传图片

自动上传图片设置

接着在自定义命令中,输入命令。如以下示例:

Python脚本(Windows)

1
python C:\Users\<YOUR_USER_NAME>\Desktop\uploadImg.py C:\Users\<YOUR_USER_NAME>\Desktop\ImageAssetGit ${filepath}

Shell(MacOS)

1
/User/<YOUR_USER_NAME>/code/uploadImg.sh /User/<YOUR_USER_NAME>/Desktop/ImageAssetGit/ ${filepath}

这里${filepath}是Typora提供的一个参数,在执行自定义命令时,会将当前操作的Typora文档的文件名,以参数的形式传递进来。

如果是在刚新建的、且还未保存文件至本地磁盘的markdown文档中插入图片,执行上传图片操作,在调用该自定义命令时,Typora将传一个空值。在Typora偏好设置点击验证图片上传选项,测试自定义命令时,Typora传递的也是空值。

另外,${filename}也是Typora提供的一个参数,它传递的则是当前操作的Typora文档的文件名。

测试运行

完成脚本和设置偏好后,点击验证图片上传选项,进行测试。

验证图片上传选项

如果你看到这个画面,说明Typora的测试通过。

上传验证成功

如果不行,要看看是否有其他需要处理的事情。比如在MacOS或Linux系统,需要使用chmod命令给脚本执行文件授权等。

再将图片复制进Typora后,点击图片,右键,选择上传图片。此时将会执行自定义的图片上传脚本。脚本执行结束后,图片的引用链接将会被修改。

编辑时手动上传图片

进入到本地图床git仓库的目录下查看,图片也复制了一份过来,说明脚本运行正常。

文件成功复制

脚本执行成功后,图片文件将被移动到指定的本地git仓库目录下,可以看到图片的引用也被修改了。

上传完成

但它似乎没有正常显示图片。但不着急,因为我们自建的图床还没有更新。

进入本地图床的git仓库目录,通过git工具,或在bash执行命令,手动上传至私有云图库即可。网络图床更新后,重新打开文件,图片正常显示了。

git bash命令如下:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
#Windows
cd C:\\Users\\<YOUR_USER_NAME>\\Desktop\\ImageAssetGit
git add .
git commit -m "upload Img"

#MacOS
cd /User/<YOUR_USER_NAME>/Desktop/ImageAssetGit/
git add .
git commit -m "upload Img"

使用git搭建私有云图床,请另参考文章:【】

参考


Typora自定义命令上传图片
http://blog.cnctema.pub/post/zh-CN/ddc70777d047/
作者
cnctema
发布于
2022年9月24日
许可协议