开发一款Typora自定义图片上传命令行工具,上传到你自己的图床!

为什么要开发这款工具?

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 的图片上传服务:

最后测试一下是否成功:

可以看见成功上传了。图片都在仓库里面:

相关推荐
zaim15 小时前
计算机的错误计算(一百一十四)
java·c++·python·rust·go·c·多项式
百里守约学编程20 小时前
70. 爬楼梯
算法·leetcode·go
a_ran2 天前
一些 Go Web 开发笔记
后端·golang·go·编程·web·网站
影灵衣丶2 天前
go进阶编程:设计模式之适配器模式
后端·go
StevenZeng学堂2 天前
【云原生安全篇】Cosign助力Harbor验证镜像实践
网络·安全·云原生·容器·kubernetes·云计算·go
代吗喽2 天前
Macos终端常用的命令行指令总结
macos·命令行
qq_172805593 天前
GO Message Bus
开发语言·后端·golang·go
一丝晨光3 天前
void类型
java·开发语言·javascript·c++·c#·go·c
IT杨秀才4 天前
自己动手写了一个协程池
后端·程序员·go
狼爷4 天前
解开 Golang‘for range’的神秘面纱:易错点剖析与解读
go