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

最后测试一下是否成功:

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

相关推荐
煎鱼eddycjy5 小时前
新提案:由迭代器启发的 Go 错误函数处理
go
煎鱼eddycjy5 小时前
Go 语言十五周年!权力交接、回顾与展望
go
不爱说话郭德纲1 天前
聚焦 Go 语言框架,探索创新实践过程
go·编程语言
0x派大星2 天前
【Golang】——Gin 框架中的 API 请求处理与 JSON 数据绑定
开发语言·后端·golang·go·json·gin
IT书架2 天前
golang高频面试真题
面试·go
郝同学的测开笔记2 天前
云原生探索系列(十四):Go 语言panic、defer以及recover函数
后端·云原生·go
秋落风声3 天前
【滑动窗口入门篇】
java·算法·leetcode·go·哈希表
x-cmd4 天前
x-cmd pkg | helix - 用 Rust 打造的文本编辑器,内置 LSP 和语法高亮,兼容 Vim 用户习惯
运维·rust·vim·开发·lsp·命令行·文本编辑
0x派大星5 天前
【Golang】——Gin 框架中的模板渲染详解
开发语言·后端·golang·go·gin
0x派大星5 天前
【Golang】——Gin 框架中的表单处理与数据绑定
开发语言·后端·golang·go·gin