前言 之前一直使用Typora + Picgo
上传图片,但是在启动Picgo.exe
是会卡死不动。然后最近想学习一下Go
开发,那么就用Go
开发一个上传图片到图床的插件。这里配合SM.MS
图床
SM.MS的API 在SM.MS的官方文档里详细介绍了上传图片的API
使用,详细可以参考https://doc.sm.ms/#api-Image-Upload。
API调用的地址为https://sm.ms/api/v2/
上传功能 根据SM.MS
的API
文档介绍,我们大致理一下思路,需要提供的条件如下
地址:"https://sm.ms/api/v2/upload"
方法:POST
,请求
头部
头部类型默认为:multipart/form-data
认证码为从SM.MS
获取
因此首先需要利用Go
语言发出POST
请求,首选需要导入net/http
库,从而可以使用http
库。
通过NewRequest
函数生成一个请求
创建Client
结构体
发送请求
获取响应
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 import ( "net/http" "io" ) ... req, err := http.NewRequest("POST" , url, body) if err != nil { fmt.Println("Error creating request:" , err) return "" } client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println("Error sending request:" , err) return "" } defer resp.Body.Close() responseBody, err := io.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response:" , err) return "" } ...
在发送请求前,我们需要构造符合条件的body
以及header
,首先从请求中取出Header
在利用Set
函数设置头部信息。
1 2 3 req.Header.Set("Content-Type" , writer.FormDataContentType()) req.Header.Set("Authorization" , "xxxxxx" )
body
则需要将参数存储进去,首先存储图片文件,这里需要导入os
库利用Open
方法打开图片文件,defer
关键字是当脱离当前函数域是会自动执行的操作。紧接着创建一段缓冲区,用于存放body
。创建表单类型的写入对象,将文件名写入,紧接着将文件数据写入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 file, err := os.Open(imagePath)if err != nil { fmt.Println("Error opening file:" , err) }defer file.Close() body := &bytes.Buffer{} writer := multipart.NewWriter(body) imagePart, err := writer.CreateFormFile("smfile" , imagePath)if (err != nil ) { fmt.Println("Error creating form file:" , err) return "" } _, err = io.Copy(imagePart, file)if (err != nil ) { fmt.Println("Error writing file data to form field: " , err) return "" }
写入普通的参数则直接写入即可,利用WriteField
函数。
1 2 3 4 5 6 7 8 9 10 11 err = writer.WriteField("format" , stringParam)if err != nil { fmt.Println("Error writing form field:" , err) return "" } err = writer.Close()if err != nil { fmt.Println("Error closing form writer:" , err) return "" }
最后就是如何从响应中获取图片的路径,观察到在响应体中存在url
的字段。
首先我们在body
设置了返回的类型为JSON
,因此首要的是将响应解析为JSON
格式。利用make创建映射,映射类型为string
->任意类型,这里的interface{}
是指空接口,类似与泛型,但是与泛型不同的是,它不会对类型进行校验。紧接着将响应包解析为JSON
格式,这里的[]byte
是定义字节的切片,类似于字节数组。
1 2 3 4 5 6 7 8 responseJson := make (map [string ]interface {}) err = json.Unmarshal([]byte (responseBody), &responseJson)if err != nil { fmt.Println("Error Json" , err) return "" }
可以看到url
是嵌入到message
对象中的,因此是JSON
格式中对象的对象。
1 2 3 4 5 6 7 8 9 10 11 12 data, ok := responseJson["data" ].(map [string ]interface {})if !ok { fmt.Println("data does not exit" ) return "" } urlData, ok := data["url" ].(string )if !ok { fmt.Println("url does not exit" ) return "" }
至此上传功能就完成了。
下载功能 这里下载的功能是指若Typora
中的图床链接是远程的,想要将图片批量下载的本地所提供的功能。
首先是设置代理,因为像Github
等图床链接不设置代理会因为网速的问题下载失败。
1 2 3 4 5 6 7 8 9 10 proxyURL, err := url.Parse("http://xxxx:xxx" )if err != nil { fmt.Println("Error Set Proxy" ) return "" } http.DefaultTransport = &http.Transport{ Proxy: http.ProxyURL(proxyURL), }
设置完代理之后,就需要传入需要下载的图片路径,通过Http
的Get
请求获取,然后从响应中获取图片数据,这里为了方便,还指定了存放文件的路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 resp, err := http.Get(imageUrl)if err != nil { fmt.Println("Error Http" , err) return "" }defer resp.Body.Close() fileName := filepath.Base(imageUrl) file, err := os.Create(directoryPath + "\\" + fileName)if err != nil { fmt.Println("Error Create File" , err) return "" }defer file.Close() _ , err = io.Copy(file, resp.Body)if err != nil { fmt.Println("Error Copy Body " , err) return "" }
文件扫描功能 由于该工具是需要扫描文件,提取图片路径完成上传或者下载功能。
首先打开需要上传或者下载的文件,创建扫描器逐行提取内容,匹配图片的markdown
格式,然后再次匹配获取括号内的内容,提取图片路径,将路径传入上传或下载功能,最后修改原本的图片路径为新路径。
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 ... targetFile := os.Args[1 ] file , err := os.Open(targetFile)if err != nil { fmt.Println("Error opening file:" , err) return }defer file.Close() ... scanner := bufio.NewScanner(file) pattern := regexp.MustCompile(`!\[(.*?)\]\((.*?)\)` )var newLines []string for scanner.Scan() { line := scanner.Text() if pattern.MatchString(line){ fmt.Println("Match found:" , line) re := regexp.MustCompile(`\(([^\(\)]*)\)` ) match := re.FindStringSubmatch(line) path := match[1 ] ... line = strings.Replace(line, match[1 ], url, -1 )