为什么要开发这款工具?
Typora 是一款 IT 界常用的 Markdown 笔记软件,它有强大的 Markdown 语法功能。同时它也可以粘贴图片、音频、视频等媒体文件。
通常,从剪切板粘贴的图片都会默认保存到本地的 assets 同级目录中,文件名称为截图工具生成的名称。保存在本地虽然说可以长时间保存,但是如果你要迁移你的文档到其他平台的时候就很麻烦,比如说语雀、石墨等平台。
如果你使用 Typora 提供的上传服务那得要架梯子,文件存储到国外服务器,不会科学上网的小伙伴就麻烦了。或者你公司可能要求所有文档相关图片要上传到公司的服务器,你也得自己搞一个上传工具。
选择作为图床的平台
适合作为图床的平台通常可以是云服务厂商提供的 OSS 存储服务平台、代码托管平台、公司自己搭建的 OSS 服务平台。
下面以 Gitee 为例,调用 Gitee 的开发者 API 上传图片。
Gitee
Gitee 开发者文档
首先我们需要打开 Gitee 的开发者文档官网:Gitee API 文档。然后找到上传文件的 API:
而且之前你还需要获得 Gitee API 的授权,就在页面的右上角点击获取授权,笔者这里已经获取授权了。
在图中,可以看到几个参数:
- access_token:用户授权码,相当于令牌。
- owner:是你的注册账号的用户名不是昵称。
- repo:仓库名。
- path:文件在仓库里面存储的路径。
- content:文件的 base64 编码。
- message:提交的消息。
- branch:分支,默认是你的主分支。
接下来,先去获取用户授权码。点击设置,找到用户账号设置,找到私人令牌:
创建令牌完成了之后,把令牌内容保存下来,因为只显示一次。
Typora 图片上传命令行文档
上述工作完成之后,找到 Typora 图片上传命令行文档:
简单来讲,Typora 在你粘贴图片的时候会执行像这样的命令:
shell
[some path]/upload-image.sh "image-path-1" "image-path-2"
这个命令后面跟着多个参数表示多个图片文件。
最后你需要在控制台打印,如下内容:
shell
Upload Success:
http://remote-image-1.png
http://remote-image-2.png
编写代码
在了解如上条件之后,我们接下来就开始编写代码。笔者使用 Go 开发这款工具,因为 Go 代码可以直接编译成二进制文件,正好符合 Typora 的要求。
定义响应体结构体类型:
go
package main
type UploadFileResponse struct {
Commit Commit `json:"commit"`
Content Content `json:"content"`
}
type Commit struct {
Sha string `json:"sha,omitempty"`
Author string `json:"author,omitempty"`
Committer string `json:"committer,omitempty"`
Message string `json:"message,omitempty"`
Tree string `json:"tree,omitempty"`
Parents string `json:"parents,omitempty"`
}
type Content struct {
Name string `json:"name,omitempty"`
Path string `json:"path,omitempty"`
Size string `json:"size,omitempty"`
Sha string `json:"sha,omitempty"`
Type string `json:"type,omitempty"`
Url string `json:"url,omitempty"`
HtmlUrl string `json:"html_url,omitempty"`
DownloadUrl string `json:"download_url,omitempty"`
Links string `json:"_links,omitempty"`
}
编写主函数:
go
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"text/template"
"time"
)
const (
giteeUploadApiTemplate = "https://gitee.com/api/v5/repos/{{.owner}}/{{.repo}}/contents/{{.path}}"
owner = "Lob" // 用户名
repo = "image-bed" // 图床仓库名称
accessToken = "1ba4ac9113e5153bd128dc6" // accessToken改成你自己的
message = "图床上传图片"
branch = "master"
)
func uploadPic(path, content string) (string, error) {
tmplData := map[string]any{
"owner": owner,
"repo": repo,
"path": path,
}
formData := url.Values{
"access_token": {accessToken},
"content": {content},
"message": {message},
"branch": {branch},
}
tmpl, err := template.New("tmpl").Parse(giteeUploadApiTemplate)
if err != nil {
log.Println("Error parsing template:", err)
return "", errors.New("error parsing template")
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, tmplData)
if err != nil {
log.Println("Error parsing template:", err)
return "", errors.New("error parsing template")
}
api := buf.String()
req, err := http.NewRequest(http.MethodPost, api, bytes.NewBufferString(formData.Encode()))
if err != nil {
log.Println("Error creating request:", err)
return "", errors.New("error creating request")
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(req)
defer resp.Body.Close()
if err != nil {
log.Println("Error sending request:", err)
return "", errors.New("error creating request")
}
byteArr, _ := io.ReadAll(resp.Body)
u := &UploadFileResponse{}
json.Unmarshal(byteArr, u)
return u.Content.DownloadUrl, nil
}
func main() {
parameters := os.Args[1:]
var picUrls []string
for _, parameter := range parameters {
now := time.Now()
// 反人类的时间格式
path := fmt.Sprintf("%s.png", now.Format("2006-01-02 15:04:05"))
data, err := os.ReadFile(parameter)
if err != nil {
log.Printf("read file failed: %s", parameter)
}
content := base64.StdEncoding.EncodeToString(data)
picUrl, err := uploadPic(path, content)
if err != nil {
return
}
picUrls = append(picUrls, picUrl)
}
fmt.Println("Upload Success:")
for _, picUrl := range picUrls {
fmt.Println(picUrl)
}
}
编译测试
shell
go build -o /Users/element/go/bin/gitee-uploader
编译并保存到 GOPATH 下面,然后设置 Typora 的图片上传服务:
最后测试一下是否成功:
可以看见成功上传了。图片都在仓库里面: