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


highlight: xcode

theme: vuepress

iShot<em>2024-02-15</em>13.10.30.png

为什么要开发这款工具?

Typora 是一款 IT 界常用的 Markdown 笔记软件,它有强大的 Markdown 语法功能。同时它也可以粘贴图片、音频、视频等媒体文件。

通常,从剪切板粘贴的图片都会默认保存到本地的 assets 同级目录中,文件名称为截图工具生成的名称。保存在本地虽然说可以长时间保存,但是如果你要迁移你的文档到其他平台的时候就很麻烦,比如说语雀、石墨等平台。

如果你使用 Typora 提供的上传服务那得要架梯子,文件存储到国外服务器,不会科学上网的小伙伴就麻烦了。或者你公司可能要求所有文档相关图片要上传到公司的服务器,你也得自己搞一个上传工具。

选择作为图床的平台

适合作为图床的平台通常可以是云服务厂商提供的 OSS 存储服务平台、代码托管平台、公司自己搭建的 OSS 服务平台。

下面以 Gitee 为例,调用 Gitee 的开发者 API 上传图片。

Gitee

Gitee 开发者文档

首先我们需要打开 Gitee 的开发者文档官网:Gitee API 文档。然后找到上传文件的 API:

image.png

而且之前你还需要获得 Gitee API 的授权,就在页面的右上角点击获取授权,笔者这里已经获取授权了。

在图中,可以看到几个参数:

  • access_token:用户授权码,相当于令牌。
  • owner:是你的注册账号的用户名不是昵称。
  • repo:仓库名。
  • path:文件在仓库里面存储的路径。
  • content:文件的 base64 编码。
  • message:提交的消息。
  • branch:分支,默认是你的主分支。

接下来,先去获取用户授权码。点击设置,找到用户账号设置,找到私人令牌:

image.png

创建令牌完成了之后,把令牌内容保存下来,因为只显示一次。

Typora 图片上传命令行文档

上述工作完成之后,找到 Typora 图片上传命令行文档:

image.png

image.png

简单来讲,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 { path := fmt.Sprintf("%s.png", time.Now().UnixMicro())

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

image.png

最后测试一下是否成功:

image.png

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

image.png

阿里云 OSS

下面来介绍如何使用阿里云 OSS 来存储文件。阿里云 OSS 相较于 Gitee 平台传输时延更低、响应更快。

进入阿里云控制台

首先需要进入阿里云控制台,找到 OSS 服务。点击右下角的 Access Key 按钮,去创建 Access Key。

image.png

然后点击 API 文档,找到开发参考的 Go 的代码:

image.png

安装依赖

输入命令,安装阿里云 OSS 的 SDK 依赖:

shell go get github.com/aliyun/aliyun-oss-go-sdk

编写代码

```go package main

import (
"context"
"fmt"
"os"
"time"

"github.com/aliyun/aliyun-oss-go-sdk/oss"

)

const (
endPoint = "https://oss-cn-shanghai.aliyuncs.com" // 接入点
accessKeyId = "LTAI4G3DN5kdk7jbe9hGfEuc" // 密钥ID
accessKeySecret = "7RzUrCyhsH1aAWFLRaAMtsONb3V0RG" // 密钥
bucketName = "codeart-oss" // 桶名
accessAddress = "https://codeart-oss.oss-cn-shanghai.aliyuncs.com" // 接入的地址
)

func main() {
parameters := os.Args[1:]
client, err := oss.New(endPoint, accessKeyId, accessKeySecret)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}

bucket, err := client.Bucket(bucketName)  
if err != nil {  
    fmt.Println("Error:", err)  
    os.Exit(-1)  
}  

ctx := context.Background()  
// 指定请求上下文过期时间。  
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)  
defer cancel()  

// 将本地文件上传至OSS。  
var picUrls []string  
for _, parameter := range parameters {  
    objectKey := fmt.Sprintf("%d.png", time.Now().UnixMicro())  
    err = bucket.PutObjectFromFile(objectKey, parameter, oss.WithContext(ctx))  
    if err != nil {  
        select {  
        case <-ctx.Done():  
            fmt.Println("Request cancelled or timed out")  
        default:  
            fmt.Println("Upload fail, Error:", err)  
        }  
        continue  
    }  
    picUrl := fmt.Sprintf("%s/%s", accessAddress, objectKey)  
    picUrls = append(picUrls, picUrl)  
}  
fmt.Println("Upload Success:")  
for _, picUrl := range picUrls {  
    fmt.Println(picUrl)  
}

} ```

编译测试

输入命令编译:

shell go build -o /Users/element/go/bin/aliyun-uploader

同样来测试一下是否成功:

image.png

图片可以在阿里云控制台看到:

image.png

参考源码

源码地址:typora-pic-uploader: 使用 Go 编写的 Tyora 自定义图片上传命令行工具 (gitee.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值