├── LICENSE
├── .gitignore
├── logo.png
├── .github
├── PULL_REQUEST_TEMPLATE.md
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ ├── go-build-test.yml
│ ├── go-binary-release.yml
│ └── golangci-lint.yml
├── tools
├── upload
│ ├── req.json
│ ├── resp.json
│ ├── upload_test.go
│ └── upload.go
├── download
│ ├── urls.go
│ ├── urls_test.go
│ ├── download_test.go
│ └── download.go
└── regs
│ ├── regs.go
│ └── regs_test.go
├── main.go
├── go.mod
├── go.sum
├── Makefile
├── cmd
├── du.go
├── root.go
├── u.go
└── d.go
├── README.md
└── .golangci.yaml
/LICENSE:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/tmp
2 | **/images
3 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarvanstack/markpic/HEAD/logo.png
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tools/upload/req.json:
--------------------------------------------------------------------------------
1 | {
2 | "list": [
3 | "C:\\Users\\dengjiawen\\Downloads\\yuque_diagram.jpg"
4 | ]
5 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Jarvan的博客
4 | url: https://jarvans.com
5 | about: 大厂程序员, 公众号 硬核的Jarvan
6 |
--------------------------------------------------------------------------------
/tools/upload/resp.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "result": [
4 | "https://markdown-1304103443.cos.ap-guangzhou.myqcloud.com/2022-02-04yuque_diagram.jpg"
5 | ]
6 | }
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2022 NAME HERE
3 |
4 | */
5 | package main
6 |
7 | import "github.com/jarvanstack/markpic/cmd"
8 |
9 | func main() {
10 | cmd.Execute()
11 | }
12 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/jarvanstack/markpic
2 |
3 | go 1.18
4 |
5 | require github.com/spf13/cobra v1.6.1
6 |
7 | require (
8 | github.com/inconshreveable/mousetrap v1.0.1 // indirect
9 | github.com/spf13/pflag v1.0.5 // indirect
10 | )
11 |
--------------------------------------------------------------------------------
/tools/download/urls.go:
--------------------------------------------------------------------------------
1 | package download
2 |
3 | import "net/url"
4 |
5 | // TrimUrl 删除 URL 中的参数
6 | func TrimUrl(urlStr string) string {
7 | u, err := url.Parse(urlStr)
8 | if err != nil {
9 | panic(err)
10 | }
11 | return u.Scheme + "://" + u.Host + u.Path
12 | }
13 |
--------------------------------------------------------------------------------
/.github/workflows/go-build-test.yml:
--------------------------------------------------------------------------------
1 | name: Go package
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v3
11 |
12 | - name: Set up Go
13 | uses: actions/setup-go@v4
14 | with:
15 | go-version: '1.15'
16 |
17 | - name: Build
18 | run: go build -v ./...
19 |
20 | - name: Test
21 | run: go test -json > TestResults-${{ matrix.go-version }}.json
22 | - name: Upload Go test results
23 | uses: actions/upload-artifact@v3
24 | with:
25 | name: Go-results-${{ matrix.go-version }}
26 | path: TestResults-${{ matrix.go-version }}.json
27 | retention-days: 5
--------------------------------------------------------------------------------
/.github/workflows/go-binary-release.yml:
--------------------------------------------------------------------------------
1 | name: Auto Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | permissions:
9 | contents: write
10 |
11 | jobs:
12 | goreleaser:
13 | runs-on: ubuntu-latest
14 | steps:
15 | -
16 | name: Checkout
17 | uses: actions/checkout@v2
18 | with:
19 | fetch-depth: 0
20 | -
21 | name: Set up Go
22 | uses: actions/setup-go@v2
23 | with:
24 | go-version: 1.17
25 | -
26 | name: Run GoReleaser
27 | uses: goreleaser/goreleaser-action@v2
28 | with:
29 | # either 'goreleaser' (default) or 'goreleaser-pro'
30 | distribution: goreleaser
31 | version: latest
32 | args: release --rm-dist
33 | env:
34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
2 | github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
3 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
4 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
5 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
6 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
7 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
8 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
9 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
11 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 请求新功能 Request new features
3 | about: 提出你期待的功能特性 Come up with the features you expect
4 | ---
5 |
6 | ### 你在什么场景下需要该功能? In what scenarios do you need this function?
7 |
8 |
12 |
13 | ### 描述最优的解决方案 Describe the optimal solution
14 |
15 |
19 |
20 | ### 描述候选解决方案 Describe the candidate solution
21 |
22 |
26 |
27 | ### 其他信息 Other information
28 |
29 |
--------------------------------------------------------------------------------
/tools/regs/regs.go:
--------------------------------------------------------------------------------
1 | package regs
2 |
3 | import (
4 | "regexp"
5 | "strings"
6 | )
7 |
8 | var imgUrlReg = regexp.MustCompile(`!\[.*\]\((.*)\)`)
9 |
10 | // GetRemoteImg 获取markdown中的远程图片链接
11 | func GetRemoteImg(text string) []string {
12 | sss := imgUrlReg.FindAllStringSubmatch(text, -1)
13 |
14 | var urls []string
15 | for _, ss := range sss {
16 | for _, s := range ss {
17 | if strings.HasPrefix(s, "![") {
18 | continue
19 | }
20 | if strings.HasPrefix(s, "http") {
21 | urls = append(urls, s)
22 | }
23 | }
24 | }
25 |
26 | return urls
27 | }
28 |
29 | // GetRemoteImg 获取markdown中的所有本地图片链接
30 | func GetLocalImg(text string) []string {
31 | sss := imgUrlReg.FindAllStringSubmatch(text, -1)
32 |
33 | var urls []string
34 | for _, ss := range sss {
35 | for _, s := range ss {
36 | if strings.HasPrefix(s, "![") {
37 | continue
38 | }
39 | if strings.HasPrefix(s, "http") {
40 | continue
41 | }
42 | urls = append(urls, s)
43 | }
44 | }
45 |
46 | return urls
47 | }
48 |
--------------------------------------------------------------------------------
/tools/download/urls_test.go:
--------------------------------------------------------------------------------
1 | package download
2 |
3 | import "testing"
4 |
5 | func TestTrim(t *testing.T) {
6 | type args struct {
7 | urlStr string
8 | }
9 | tests := []struct {
10 | name string
11 | args args
12 | want string
13 | }{
14 | {
15 | args: args{
16 | urlStr: `https://cdn.nlark.com/yuque/0/2022/png/2397010/1668767432343-635ad8ea-c4ac-42bd-ae37-91d6bf00d541.png#averageHue=%2370673e&clientId=ue647dcef-b8ea-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=1040&id=u0b01f546&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1040&originWidth=1920&originalType=binary&ratio=1&rotation=0&showTitle=false&size=134394&status=done&style=none&taskId=u71a2fad7-b512-44b5-85a2-3f682520dca&title=&width=1920`,
17 | },
18 | want: "/80/v2-aaa43d382d9acc01cfe060b0228f58c4_720w.webp",
19 | },
20 | }
21 | for _, tt := range tests {
22 | t.Run(tt.name, func(t *testing.T) {
23 | if got := TrimUrl(tt.args.urlStr); got != tt.want {
24 | t.Errorf("Trim() = %v, want %v", got, tt.want)
25 | }
26 | })
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tools/upload/upload_test.go:
--------------------------------------------------------------------------------
1 | package upload
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | /*
8 | curl --location --request POST 'http://127.0.0.1:36677/upload' --header 'Content-Type: application/json' --data-raw '{
9 | "list": [
10 | "/mnt/c/Users/dengjiawen/Downloads/ddd1.md"
11 | ]
12 | }'
13 | */
14 |
15 | func TestUploaderImpl_Upload(t *testing.T) {
16 | u := NewUploader()
17 | type args struct {
18 | localPath string
19 | }
20 | tests := []struct {
21 | name string
22 | args args
23 | wantErr bool
24 | }{
25 | {
26 | args: args{
27 | localPath: `C:\c_code\markpic\images\1668760978594-47a93e74-e87e-49a3-946c-466ffd4f952d.jpeg`,
28 | },
29 | wantErr: false,
30 | },
31 | }
32 | for _, tt := range tests {
33 | t.Run(tt.name, func(t *testing.T) {
34 | got, err := u.Upload(tt.args.localPath)
35 | if (err != nil) != tt.wantErr {
36 | t.Errorf("UploaderImpl.Upload() error = %v, wantErr %v", err, tt.wantErr)
37 | return
38 | }
39 | t.Logf("UploaderImpl.Upload() = %v", got)
40 | })
41 | }
42 | }
43 |
44 | func TestLocalIp(t *testing.T) {
45 | got := LocalIp()
46 | t.Logf("LocalIp() = %v", got)
47 | }
48 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # config
2 | bindir = ./bin
3 | releaseDir = ./release
4 | mainFile = ./main.go
5 |
6 | # var
7 | make_dir:=$(shell pwd)
8 | app_name:=$(shell basename $(make_dir))
9 | version=$(shell git describe --tags --always)
10 |
11 | ## build: Builds the project
12 | .PHONY: build
13 | build:
14 | GOOS=linux GOARCH=amd64 go build -o $(bindir)/$(app_name).linux $(mainFile)
15 | GOOS=darwin GOARCH=amd64 go build -o $(bindir)/$(app_name).darwin $(mainFile)
16 | GOOS=windows GOARCH=amd64 go build -o $(bindir)/$(app_name).exe $(mainFile)
17 |
18 | ## release: Builds the project for release
19 | .PHONY: release
20 | release: clean build
21 | tar -zcvf $(releaseDir)/$(app_name)-linux-$(version).tar.gz $(bindir)/$(app_name).linux
22 | tar -zcvf $(releaseDir)/$(app_name)-darwin-$(version).tar.gz $(bindir)/$(app_name).darwin
23 | tar -zcvf $(releaseDir)/$(app_name)-exe-$(version).tar.gz $(bindir)/$(app_name).exe
24 | git tag -d $(version)
25 | ## clean: Cleans the project
26 | .PHONY: clean
27 | clean:
28 | rm -rf $(bindir)/*
29 | rm -rf $(releaseDir)/*
30 | rm -rf images/*
31 | go clean
32 |
33 | ## help: Show this help info.
34 | .PHONY: help
35 | help: Makefile
36 | @printf "\nUsage: make ...\n\nTargets:\n"
37 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /'
38 | @echo "$$USAGE_OPTIONS
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 报告缺陷 Bug Report
3 | about: 报告缺陷以帮助我们改进 Report defects to help us improve
4 | ---
5 |
6 | ### 描述问题 Describe the problem
7 |
8 |
12 |
13 | ````````markdown
14 | 如果是解析渲染问题的话请在此处贴入 Markdown 原文
15 | If it is a problem of parsing and rendering, please post the original Markdown here
16 | ````````
17 |
18 | ### 期待的结果 Expected result
19 |
20 |
24 |
25 | ### 截屏或录像 Screenshot or video
26 |
27 |
34 |
35 | ### 版本环境 Version environment
36 |
37 | * 版本 Version:
38 | * 操作系统 Operating system:
39 | * 浏览器(如果使用)Browser (if used):
40 |
41 | ### 其他信息 Other information
42 |
43 |
--------------------------------------------------------------------------------
/cmd/du.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2022 NAME HERE
3 | */
4 | package cmd
5 |
6 | import (
7 | "fmt"
8 |
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | // duCmd represents the markpic command
13 | var duCmd = &cobra.Command{
14 | Use: "du",
15 | Short: "先 d(下载) 再 u(上传)",
16 | Run: func(cmd *cobra.Command, args []string) {
17 | from := cmd.Flag("from").Value.String()
18 | dir := cmd.Flag("dir").Value.String()
19 |
20 | err := du(from, dir)
21 | if err != nil {
22 | fmt.Println(err)
23 | return
24 | }
25 |
26 | },
27 | }
28 |
29 | func du(from, dir string) error {
30 | fmt.Println("[下载-上传]")
31 |
32 | toLocal := from + downloadFilePrefix
33 | err := d(from, toLocal, dir)
34 | if err != nil {
35 | fmt.Println(err)
36 | return err
37 | }
38 |
39 | fromLocal := toLocal
40 | toRemote := from + uploadFilePrefix
41 | err = u(fromLocal, toRemote)
42 | if err != nil {
43 | fmt.Println(err)
44 | return err
45 | }
46 |
47 | fmt.Println("[下载-上传完成]", fromLocal, toRemote)
48 |
49 | return nil
50 | }
51 |
52 | func init() {
53 | rootCmd.AddCommand(duCmd)
54 |
55 | // Here you will define your flags and configuration settings.
56 |
57 | // Cobra supports Persistent Flags which will work for this command
58 | // and all subcommands, e.g.:
59 | // duCmd.PersistentFlags().String("foo", "", "A help for foo")
60 |
61 | // Cobra supports local flags which will only run when this command
62 | // is called directly, e.g.:
63 | // duCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
64 | }
65 |
--------------------------------------------------------------------------------
/cmd/root.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2022 NAME HERE
3 |
4 | */
5 | package cmd
6 |
7 | import (
8 | "fmt"
9 | "os"
10 |
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | var (
15 | downloadFilePrefix = ".download.md"
16 | uploadFilePrefix = ".upload.md"
17 | )
18 |
19 | var (
20 | from string
21 | dir string
22 | )
23 |
24 | // rootCmd represents the base command when called without any subcommands
25 | var rootCmd = &cobra.Command{
26 | Use: "markpic [command]",
27 | Short: "一键将 markdown 中的所有图片下载到本地, 并通过 picgo 上传到图床",
28 | Args: cobra.MatchAll(cobra.ArbitraryArgs), // 任意参数
29 | Run: func(cmd *cobra.Command, args []string) {
30 | var from, dir string
31 |
32 | if len(args) > 0 {
33 | from = args[0]
34 | } else {
35 | from = cmd.Flag("from").Value.String()
36 | }
37 |
38 | if len(args) > 1 {
39 | dir = args[1]
40 | } else {
41 | dir = cmd.Flag("dir").Value.String()
42 | }
43 |
44 | err := du(from, dir)
45 | if err != nil {
46 | fmt.Println(err)
47 | return
48 | }
49 | },
50 | }
51 |
52 | // Execute adds all child commands to the root command and sets flags appropriately.
53 | // This is called by main.main(). It only needs to happen once to the rootCmd.
54 | func Execute() {
55 | err := rootCmd.Execute()
56 | if err != nil {
57 | os.Exit(1)
58 | }
59 | }
60 |
61 | func init() {
62 | // Here you will define your flags and configuration settings.
63 | // Cobra supports persistent flags, which, if defined here,
64 | // will be global for your application.
65 |
66 | rootCmd.PersistentFlags().StringVarP(&from, "from", "f", "source.md",
67 | "需要处理的源文件")
68 | rootCmd.PersistentFlags().StringVarP(&dir, "dir", "d", "images/",
69 | "图片存放的目录")
70 |
71 | // Cobra also supports local flags, which will only run
72 | // when this action is called directly.
73 | // rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/tools/download/download_test.go:
--------------------------------------------------------------------------------
1 | package download
2 |
3 | import (
4 | "os"
5 | "testing"
6 | )
7 |
8 | func TestDownLoadImpl_DownLoad(t *testing.T) {
9 | var dir string = "tmp/"
10 |
11 | // 删除临时文件
12 | os.RemoveAll(dir)
13 |
14 | d := NewDownLoader(dir)
15 |
16 | type args struct {
17 | url string
18 | }
19 | tests := []struct {
20 | name string
21 | args args
22 | want string
23 | wantErr bool
24 | }{
25 | {
26 | args: args{
27 | url: `https://pic1.zhimg.com/80/v2-aaa43d382d9acc01cfe060b0228f58c4_720w.webp`,
28 | },
29 | want: dir + "v2-aaa43d382d9acc01cfe060b0228f58c4_720w.webp",
30 | wantErr: false,
31 | },
32 | {
33 | args: args{
34 | url: `https://cdn.nlark.com/yuque/0/2022/png/2397010/1668767432343-635ad8ea-c4ac-42bd-ae37-91d6bf00d541.png`,
35 | },
36 | want: dir + "1668767432343-635ad8ea-c4ac-42bd-ae37-91d6bf00d541.png",
37 | wantErr: false,
38 | },
39 | {
40 | args: args{
41 | url: `https://cdn.nlark.com/yuque/__puml/2aa76dcc256f08335083bd039b5b4e49.svg#lake_card_v2=eyJ0eXBlIjoicHVtbCIsImNvZGUiOiJAc3RhcnR1bWxcblxufEEgU2VjdGlvbnxcbnN0YXJ0XG46c3RlcDE7XG58I0FudGlxdWVXaGl0ZXxCIFNlY3Rpb258XG46c3RlcDI7XG46c3RlcDM7XG58QSBTZWN0aW9ufFxuOnN0ZXA0O1xufEIgU2VjdGlvbnxcbjpzdGVwNTtcbnN0b3BcblxuQGVuZHVtbCIsInVybCI6Imh0dHBzOi8vY2RuLm5sYXJrLmNvbS95dXF1ZS9fX3B1bWwvMmFhNzZkY2MyNTZmMDgzMzUwODNiZDAzOWI1YjRlNDkuc3ZnIiwiaWQiOiJtanN4OCIsIm1hcmdpbiI6eyJ0b3AiOnRydWUsImJvdHRvbSI6dHJ1ZX0sImNhcmQiOiJkaWFncmFtIn0=`,
42 | },
43 | want: dir + "2aa76dcc256f08335083bd039b5b4e49.svg",
44 | wantErr: false,
45 | },
46 | }
47 | for _, tt := range tests {
48 | t.Run(tt.name, func(t *testing.T) {
49 | got, err := d.DownLoad(tt.args.url)
50 | if (err != nil) != tt.wantErr {
51 | t.Errorf("DownLoadImpl.DownLoad() error = %v, wantErr %v", err, tt.wantErr)
52 | return
53 | }
54 | if got != tt.want {
55 | t.Errorf("DownLoadImpl.DownLoad() = %v, want %v", got, tt.want)
56 | }
57 | })
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/tools/regs/regs_test.go:
--------------------------------------------------------------------------------
1 | package regs
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 | )
7 |
8 | func TestGetMarkdownImageUrls(t *testing.T) {
9 | type args struct {
10 | text string
11 | }
12 | tests := []struct {
13 | name string
14 | args args
15 | want []string
16 | }{
17 | {
18 | args: args{
19 | text: `- 阅读: review 自己的文章, 就像代码一样, 你没有 review 的习惯, 但是这个习惯是至关重要的, 对比, 优化, 反馈才能进步
20 | - 分享: 关于一些知识, 比如说 no tech 思考方法, 可以分享给别人
21 | - 知识网络: 类似 notion 的知识网络, 也许有用的
22 |
23 | **主要是阅读**
24 | `,
25 | },
26 | want: []string{
27 | `https://cdn.nlark.com/yuque/__puml/2aa76dcc256f08335083bd039b5b4e49.svg#lake_card_v2=eyJ0eXBlIjoicHVtbCIsImNvZGUiOiJAc3RhcnR1bWxcblxufEEgU2VjdGlvbnxcbnN0YXJ0XG46c3RlcDE7XG58I0FudGlxdWVXaGl0ZXxCIFNlY3Rpb258XG46c3RlcDI7XG46c3RlcDM7XG58QSBTZWN0aW9ufFxuOnN0ZXA0O1xufEIgU2VjdGlvbnxcbjpzdGVwNTtcbnN0b3BcblxuQGVuZHVtbCIsInVybCI6Imh0dHBzOi8vY2RuLm5sYXJrLmNvbS95dXF1ZS9fX3B1bWwvMmFhNzZkY2MyNTZmMDgzMzUwODNiZDAzOWI1YjRlNDkuc3ZnIiwiaWQiOiJtanN4OCIsIm1hcmdpbiI6eyJ0b3AiOnRydWUsImJvdHRvbSI6dHJ1ZX0sImNhcmQiOiJkaWFncmFtIn0=`,
28 | },
29 | },
30 | {
31 | args: args{
32 | text: `
33 | 
34 | 发顺丰的
35 | 
36 | `,
37 | },
38 | want: []string{
39 | "http://1.png",
40 | "http://2.png",
41 | },
42 | },
43 | }
44 | for _, tt := range tests {
45 | t.Run(tt.name, func(t *testing.T) {
46 | if got := GetRemoteImg(tt.args.text); !reflect.DeepEqual(got, tt.want) {
47 | t.Errorf("GetMarkdownImageUrls() = %v, want %v", got, tt.want)
48 | }
49 | })
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/.github/workflows/golangci-lint.yml:
--------------------------------------------------------------------------------
1 | name: golangci-lint
2 | on:
3 | push:
4 | branches:
5 | - master
6 | - main
7 | pull_request:
8 |
9 | permissions:
10 | contents: read
11 | # Optional: allow read access to pull request. Use with `only-new-issues` option.
12 | # pull-requests: read
13 |
14 | jobs:
15 | golangci:
16 | name: lint
17 | runs-on: ubuntu-latest
18 | steps:
19 | - uses: actions/checkout@v3
20 | - uses: actions/setup-go@v4
21 | with:
22 | go-version: '1.21'
23 | cache: false
24 | - name: golangci-lint
25 | uses: golangci/golangci-lint-action@v3
26 | with:
27 | # Require: The version of golangci-lint to use.
28 | # When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version.
29 | # When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit.
30 | version: v1.54
31 |
32 | # Optional: working directory, useful for monorepos
33 | # working-directory: somedir
34 |
35 | # Optional: golangci-lint command line arguments.
36 | #
37 | # Note: By default, the `.golangci.yml` file should be at the root of the repository.
38 | # The location of the configuration file can be changed by using `--config=`
39 | # args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0
40 |
41 | # Optional: show only new issues if it's a pull request. The default value is `false`.
42 | # only-new-issues: true
43 |
44 | # Optional: if set to true, then all caching functionality will be completely disabled,
45 | # takes precedence over all other caching options.
46 | # skip-cache: true
47 |
48 | # Optional: if set to true, then the action won't cache or restore ~/go/pkg.
49 | # skip-pkg-cache: true
50 |
51 | # Optional: if set to true, then the action won't cache or restore ~/.cache/go-build.
52 | # skip-build-cache: true
53 |
54 | # Optional: The mode to install golangci-lint. It can be 'binary' or 'goinstall'.
55 | # install-mode: "goinstall"
--------------------------------------------------------------------------------
/cmd/u.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2022 NAME HERE
3 | */
4 | package cmd
5 |
6 | import (
7 | "bufio"
8 | "fmt"
9 | "os"
10 | "strings"
11 |
12 | "github.com/jarvanstack/markpic/tools/regs"
13 | "github.com/jarvanstack/markpic/tools/upload"
14 |
15 | "github.com/spf13/cobra"
16 | )
17 |
18 | // uCmd represents the u command
19 | var uCmd = &cobra.Command{
20 | Use: "u",
21 | Short: "将 markdown 中的所有本地图片通过 picgo 上传到图床",
22 | Long: `将 markdown 中的所有本地图片通过 picgo 上传到图床. For example:
23 |
24 | markpic u --from README.md `,
25 | Run: func(cmd *cobra.Command, args []string) {
26 | from := cmd.Flag("from").Value.String()
27 | fmt.Println("[上传] ", from)
28 |
29 | to := from + uploadFilePrefix
30 | err := u(from, to)
31 | if err != nil {
32 | fmt.Println(err)
33 | return
34 | }
35 |
36 | fmt.Println("[上传完成]", from, to)
37 | },
38 | }
39 |
40 | func u(from, to string) error {
41 | // 输入
42 | formFile, err := os.Open(from)
43 | if err != nil {
44 | fmt.Println(err)
45 | return err
46 | }
47 | defer formFile.Close()
48 | fromBuf := bufio.NewReader(formFile)
49 |
50 | // 输出
51 | // 输出
52 | toFile, err := os.OpenFile(to, os.O_CREATE|os.O_WRONLY, 0666)
53 | if err != nil {
54 | fmt.Println(err)
55 | return err
56 | }
57 | defer toFile.Close()
58 | toBuf := bufio.NewWriter(toFile)
59 |
60 | // 下载器
61 | uploader := upload.NewUploader()
62 |
63 | isSkip := false
64 |
65 | // 读取
66 | for {
67 | line, err := fromBuf.ReadString('\n')
68 | if err != nil {
69 | break
70 | }
71 |
72 | if strings.HasPrefix(line, "```") {
73 | isSkip = !isSkip
74 | }
75 |
76 | if isSkip {
77 | _, _ = toBuf.WriteString(line)
78 | continue
79 | }
80 |
81 | // 获取 URL
82 | urls := regs.GetLocalImg(line)
83 | for _, url := range urls {
84 | // 下载
85 | newUrl, err := uploader.Upload(url)
86 | if err != nil {
87 | fmt.Println(err)
88 | return err
89 | }
90 |
91 | // 替换
92 | line = strings.ReplaceAll(line, url, newUrl)
93 | }
94 | _, err = toBuf.WriteString(line)
95 | if err != nil {
96 | fmt.Println(err)
97 | return err
98 | }
99 | }
100 | err = toBuf.Flush()
101 |
102 | return err
103 | }
104 |
105 | func init() {
106 | rootCmd.AddCommand(uCmd)
107 |
108 | // Here you will define your flags and configuration settings.
109 |
110 | // Cobra supports Persistent Flags which will work for this command
111 | // and all subcommands, e.g.:
112 | // uCmd.PersistentFlags().String("foo", "", "A help for foo")
113 |
114 | // Cobra supports local flags which will only run when this command
115 | // is called directly, e.g.:
116 | // uCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
117 | }
118 |
--------------------------------------------------------------------------------
/cmd/d.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2022 NAME HERE
3 | */
4 | package cmd
5 |
6 | import (
7 | "bufio"
8 | "fmt"
9 | "os"
10 | "strings"
11 |
12 | "github.com/jarvanstack/markpic/tools/download"
13 | "github.com/jarvanstack/markpic/tools/regs"
14 |
15 | "github.com/spf13/cobra"
16 | )
17 |
18 | // dCmd represents the d command
19 | var dCmd = &cobra.Command{
20 | Use: "d",
21 | Short: "将 markdown 中的图片下载到本地",
22 | Long: `将 markdown 中的图片下载到本地. 例如:
23 |
24 | markpic d --from README.md -dir tmp/`,
25 | Run: func(cmd *cobra.Command, args []string) {
26 | from := cmd.Flag("from").Value.String()
27 | dir := cmd.Flag("dir").Value.String()
28 | fmt.Println("[下载] ")
29 |
30 | to := from + downloadFilePrefix
31 | err := d(from, to, dir)
32 | if err != nil {
33 | fmt.Println(err)
34 | return
35 | }
36 |
37 | fmt.Println("[下载完成]", from, dir, to)
38 | },
39 | }
40 |
41 | func d(from, to, dir string) error {
42 | // 输入
43 | formFile, err := os.Open(from)
44 | if err != nil {
45 | fmt.Println(err)
46 | return err
47 | }
48 | defer formFile.Close()
49 | fromBuf := bufio.NewReader(formFile)
50 |
51 | // 输出
52 | toFile, err := os.OpenFile(to, os.O_CREATE|os.O_WRONLY, 0666)
53 | if err != nil {
54 | fmt.Println(err)
55 | return err
56 | }
57 | defer toFile.Close()
58 | toBuf := bufio.NewWriter(toFile)
59 |
60 | // 下载器
61 | downloader := download.NewDownLoader(dir)
62 |
63 | isSkip := false
64 |
65 | // 读取
66 | for {
67 | line, err := fromBuf.ReadString('\n')
68 | if err != nil {
69 | break
70 | }
71 |
72 | if strings.HasPrefix(line, "```") {
73 | isSkip = !isSkip
74 | }
75 |
76 | if isSkip {
77 | _, _ = toBuf.WriteString(line)
78 | continue
79 | }
80 |
81 | // 获取 URL
82 | urls := regs.GetRemoteImg(line)
83 | if len(urls) > 0 {
84 | for _, url := range urls {
85 | // 下载
86 | newUrl, err := downloader.DownLoad(url)
87 | if err != nil {
88 | fmt.Println(err)
89 | return err
90 | }
91 |
92 | // 替换
93 | line = strings.ReplaceAll(line, url, newUrl)
94 | }
95 | }
96 |
97 | _, _ = toBuf.WriteString(line)
98 | }
99 | _ = toBuf.Flush()
100 |
101 | return err
102 | }
103 |
104 | func init() {
105 | rootCmd.AddCommand(dCmd)
106 |
107 | // Here you will define your flags and configuration settings.
108 |
109 | // Cobra supports Persistent Flags which will work for this command
110 | // and all subcommands, e.g.:
111 | // dCmd.PersistentFlags().String("foo", "", "A help for foo")
112 |
113 | // Cobra supports local flags which will only run when this command
114 | // is called directly, e.g.:
115 | // dCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
116 | }
117 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | # markpic
12 |
13 | 一键将 markdown 中的所有图片下载到本地, 并通过 picgo 上传到图床
14 |
15 |
16 | ## 安装
17 |
18 | (1) 方法1 通过 go install 直接安装
19 |
20 | ```bash
21 | go install github.com/jarvanstack/markpic@latest
22 | ```
23 |
24 | (2) 方法2 下载编译好的二进制文件并放到 PATH 中
25 |
26 | 下载地址:
27 |
28 | ## 使用
29 |
30 | ```bash
31 | # 一键将 markdown 中的所有图片下载到本地, 并通过 picgo 上传到图床
32 | markpic test.md
33 | ```
34 |
35 | ```bash
36 | $ markpic --help
37 | 一键将 markdown 中的所有图片下载到本地, 并通过 picgo 上传到图床
38 |
39 | Usage:
40 | markpic [command] [flags]
41 | markpic [command]
42 |
43 | Available Commands:
44 | completion Generate the autocompletion script for the specified shell
45 | d 将 markdown 中的图片下载到本地
46 | du 先 d(下载) 再 u(上传)
47 | help Help about any command
48 | u 将 markdown 中的所有本地图片通过 picgo 上传到图床
49 |
50 | Flags:
51 | -d, --dir string 图片存放的目录 (default "images/")
52 | -f, --from string 需要处理的源文件 (default "source.md")
53 | -h, --help help for markpic
54 |
55 | Use "markpic [command] --help" for more information about a command.
56 | ```
57 |
58 | ## 使用实例
59 |
60 | 我们在 windows 有一个 markdown 文件 test.md
61 |
62 | ```markdown
63 | 六边形架构
64 |
65 | 
66 |
67 | ```
68 |
69 | * 里面的网络图片因为防盗链机制, 无法再其他博客平台上同步
70 | * 即使在 typroa 中设置插入图片时候对网络位置的图片上传也无法上传
71 |
72 | 
73 |
74 | * 所以我们需要将图片下载到本地, 然后再上传到图床
75 | * 但是这个操作如果图片比较多的话比较繁琐, 而且容易遗漏
76 | * 现在 **markpic 可以帮助我们一键将 markdown 中的所有图片下载到本地, 并通过 picgo 上传到图床**
77 |
78 | ```bash
79 | PS C:\c_code\markpic> markpic test.md
80 | [下载-上传]
81 | [下载-上传完成] .\test.md.download.md .\test.md.upload.md
82 | ```
83 |
84 | test.md.download.md 内容如下
85 |
86 | ```markdown
87 | 六边形架构
88 |
89 | 
90 |
91 | ```
92 |
93 | test.md.upload.md 内容如下
94 |
95 | ```markdown
96 | 六边形架构
97 |
98 | 
99 |
100 | ```
101 |
102 | 因为自己搭建的图床没有防盗链机制, 所以可以直接在其他平台同步
103 |
104 | ## 注意
105 |
106 | * 该程序调用 PicGo 客户端的接口上传图片, 使用前需要下载 PicGo 客户端并配置好图床, 保持 PicGo 客户端正常运行
107 |
108 |
109 |
--------------------------------------------------------------------------------
/tools/upload/upload.go:
--------------------------------------------------------------------------------
1 | package upload
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "fmt"
7 | "io/ioutil"
8 | "net"
9 | "net/http"
10 | "path/filepath"
11 | "runtime"
12 | "strings"
13 | )
14 |
15 | const (
16 | url = "http://localhost:36677/upload"
17 | )
18 |
19 | var (
20 | ErrPicgoUpload = errors.New("picgo 上传失败")
21 | ErrPicgoResultIsEmpty = errors.New("picgo 返回值为空")
22 | )
23 |
24 | var (
25 | ostype = runtime.GOOS
26 | )
27 |
28 | type Uploader interface {
29 | // 路径处理
30 |
31 | // 上传, 返回远程绝对路径
32 | Upload(localPath string) (string, error)
33 | }
34 |
35 | type UploaderImpl struct {
36 | }
37 |
38 | type Request struct {
39 | List []string
40 | }
41 |
42 | type Response struct {
43 | Success bool
44 | Result []string
45 | }
46 |
47 | func NewUploader() Uploader {
48 | return &UploaderImpl{}
49 | }
50 |
51 | func (u *UploaderImpl) Upload(localPath string) (string, error) {
52 | // 绝对路径
53 | localPath, err := filepath.Abs(localPath)
54 | if err != nil {
55 | return "", err
56 | }
57 |
58 | // 统一 window 和 linux 的路径分隔符
59 | if ostype == "windows" {
60 | localPath = strings.ReplaceAll(localPath, `/`, `\\`)
61 | if !strings.HasPrefix(localPath, `\\`) {
62 | localPath = strings.ReplaceAll(localPath, `\`, `\\`)
63 | }
64 | } else {
65 | localPath = strings.ReplaceAll(localPath, `\\`, "/")
66 | }
67 |
68 | fmt.Println("[上传] 绝对路径: ", localPath)
69 |
70 | return upload(localPath)
71 | }
72 |
73 | // 获取 ipv4 地址
74 | func LocalIp() string {
75 | addrs, err := net.InterfaceAddrs()
76 | if err != nil {
77 | fmt.Println(err)
78 | }
79 | var ip string = "localhost"
80 | for _, address := range addrs {
81 | if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
82 | if ipnet.IP.To4() != nil {
83 | ip = ipnet.IP.String()
84 | }
85 | }
86 | }
87 | return ip
88 | }
89 |
90 | func upload(localPath string) (string, error) {
91 |
92 | // url := "http://" + LocalIp() + ":36677/upload"
93 | url := "http://127.0.0.1:36677/upload"
94 | method := "POST"
95 |
96 | data := fmt.Sprintf(`{"list":["%s"]}`, localPath)
97 | payload := strings.NewReader(data)
98 |
99 | client := &http.Client{}
100 | req, err := http.NewRequest(method, url, payload)
101 |
102 | if err != nil {
103 | fmt.Println(err)
104 | return "", err
105 | }
106 | req.Header.Add("User-Agent", "apifox/1.0.0 (https://www.apifox.cn)")
107 | req.Header.Add("Content-Type", "application/json")
108 |
109 | res, err := client.Do(req)
110 | if err != nil {
111 | fmt.Println(err)
112 | return "", err
113 | }
114 | defer res.Body.Close()
115 |
116 | body, err := ioutil.ReadAll(res.Body)
117 | if err != nil {
118 | fmt.Println(err)
119 | return "", err
120 | }
121 |
122 | var resp Response
123 | err = json.Unmarshal(body, &resp)
124 | if err != nil {
125 | fmt.Println(err)
126 | return "", err
127 | }
128 |
129 | if len(resp.Result) == 0 {
130 | return "", ErrPicgoResultIsEmpty
131 | }
132 |
133 | return resp.Result[0], nil
134 | }
135 |
--------------------------------------------------------------------------------
/tools/download/download.go:
--------------------------------------------------------------------------------
1 | package download
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "os"
9 | "path"
10 | "path/filepath"
11 | "strconv"
12 | )
13 |
14 | type DownLoader interface {
15 | // 下载, 返回本地绝对路径
16 | DownLoad(url string) (string, error)
17 | }
18 |
19 | type DownLoaderImpl struct {
20 | dir string // 保存的目录
21 | }
22 |
23 | func NewDownLoader(dir string) DownLoader {
24 | return &DownLoaderImpl{dir: dir}
25 | }
26 |
27 | func (d *DownLoaderImpl) DownLoad(urlStr string) (string, error) {
28 | // 修剪参数
29 | urlStr = TrimUrl(urlStr)
30 |
31 | // 获取文件路径
32 | filePath, err := d.GetFilePath(urlStr)
33 | if err != nil {
34 | return "", err
35 | }
36 |
37 | // 下载
38 | err = downloadFile(urlStr, filePath, func(length, downLen int64) {
39 | })
40 | if err != nil {
41 | return "", err
42 | }
43 |
44 | // 获取相对路径
45 | relPath, err := filepath.Rel("", filePath)
46 | if err != nil {
47 | return "", err
48 | }
49 |
50 | // 转换为斜杠
51 | relPath = filepath.ToSlash(relPath)
52 |
53 | // 返回相对路径
54 | return relPath, nil
55 | }
56 |
57 | func downloadFile(url string, localPath string, fb func(length, downLen int64)) error {
58 | var (
59 | fsize int64
60 | buf = make([]byte, 32*1024)
61 | written int64
62 | )
63 | tmpFilePath := localPath + ".download"
64 | //创建一个http client
65 | client := new(http.Client)
66 | //client.Timeout = time.Second * 60 //设置超时时间
67 | //get方法获取资源
68 | resp, err := client.Get(url)
69 | if err != nil {
70 | return err
71 | }
72 |
73 | //读取服务器返回的文件大小
74 | fsize, err = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 32)
75 | if err != nil {
76 | fmt.Println(err)
77 | }
78 | //创建文件
79 | file, err := os.Create(tmpFilePath)
80 | if err != nil {
81 | return err
82 | }
83 | defer file.Close()
84 | if resp.Body == nil {
85 | return errors.New("body is null")
86 | }
87 | defer resp.Body.Close()
88 | //下面是 io.copyBuffer() 的简化版本
89 | for {
90 | //读取bytes
91 | nr, er := resp.Body.Read(buf)
92 | if nr > 0 {
93 | //写入bytes
94 | nw, ew := file.Write(buf[0:nr])
95 | //数据长度大于0
96 | if nw > 0 {
97 | written += int64(nw)
98 | }
99 | //写入出错
100 | if ew != nil {
101 | err = ew
102 | break
103 | }
104 | //读取是数据长度不等于写入的数据长度
105 | if nr != nw {
106 | err = io.ErrShortWrite
107 | break
108 | }
109 | }
110 | if er != nil {
111 | if er != io.EOF {
112 | err = er
113 | }
114 | break
115 | }
116 | //没有错误了快使用 callback
117 | fb(fsize, written)
118 | }
119 | if err == nil {
120 | file.Close()
121 | err = os.Rename(tmpFilePath, localPath)
122 | }
123 | return err
124 | }
125 |
126 | func (d *DownLoaderImpl) GetFilePath(url string) (string, error) {
127 | // 检查目录是否存在不存在就新建
128 | if _, err := os.Stat(d.dir); os.IsNotExist(err) {
129 | err = os.MkdirAll(d.dir, os.ModePerm)
130 | if err != nil {
131 | return "", err
132 | }
133 | }
134 |
135 | // 获取文件名
136 | fileName := path.Base(url)
137 | filePath := path.Join(d.dir, fileName)
138 |
139 | // 检查文件是否存在, 如果存在添加序号
140 | for i := 1; FileExist(filePath); i++ {
141 | newFileName := fmt.Sprintf("%d_", i) + fileName
142 | filePath = path.Join(d.dir, newFileName)
143 | }
144 |
145 | return filePath, nil
146 | }
147 |
148 | func FileExist(path string) bool {
149 | _, err := os.Lstat(path)
150 | return !os.IsNotExist(err)
151 | }
152 |
--------------------------------------------------------------------------------
/.golangci.yaml:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Lingfei Kong . All rights reserved.
2 | # Use of this source code is governed by a MIT style
3 | # license that can be found in the LICENSE file.
4 |
5 | # This file contains all available configuration options
6 | # with their default values.
7 |
8 | # options for analysis running
9 | run:
10 | # default concurrency is a available CPU number
11 | concurrency: 4
12 |
13 | # timeout for analysis, e.g. 30s, 5m, default is 1m
14 | timeout: 5m
15 |
16 | # exit code when at least one issue was found, default is 1
17 | issues-exit-code: 1
18 |
19 | # include test files or not, default is true
20 | tests: true
21 |
22 | # list of build tags, all linters use it. Default is empty list.
23 | build-tags:
24 | - mytag
25 |
26 | # which dirs to skip: issues from them won't be reported;
27 | # can use regexp here: generated.*, regexp is applied on full path;
28 | # default value is empty list, but default dirs are skipped independently
29 | # from this option's value (see skip-dirs-use-default).
30 | # "/" will be replaced by current OS file path separator to properly work
31 | # on Windows.
32 | skip-dirs:
33 | - test # 测试目录
34 | - tools # 工具目录
35 |
36 | # default is true. Enables skipping of directories:
37 | # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
38 | skip-dirs-use-default: true
39 |
40 | # which files to skip: they will be analyzed, but issues from them
41 | # won't be reported. Default value is empty list, but there is
42 | # no need to include all autogenerated files, we confidently recognize
43 | # autogenerated files. If it's not please let us know.
44 | # "/" will be replaced by current OS file path separator to properly work
45 | # on Windows.
46 | skip-files:
47 | - ".*\\.my\\.go$"
48 | - _test.go
49 |
50 | # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
51 | # If invoked with -mod=readonly, the go command is disallowed from the implicit
52 | # automatic updating of go.mod described above. Instead, it fails when any changes
53 | # to go.mod are needed. This setting is most useful to check that go.mod does
54 | # not need updates, such as in a continuous integration and testing system.
55 | # If invoked with -mod=vendor, the go command assumes that the vendor
56 | # directory holds the correct copies of dependencies and ignores
57 | # the dependency descriptions in go.mod.
58 | #modules-download-mode: release|readonly|vendor
59 |
60 | # Allow multiple parallel golangci-lint instances running.
61 | # If false (default) - golangci-lint acquires file lock on start.
62 | allow-parallel-runners: true
63 |
64 |
65 | # output configuration options
66 | output:
67 | # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
68 | format: colored-line-number
69 |
70 | # print lines of code with issue, default is true
71 | print-issued-lines: true
72 |
73 | # print linter name in the end of issue text, default is true
74 | print-linter-name: true
75 |
76 | # make issues output unique by line, default is true
77 | uniq-by-line: true
78 |
79 | # add a prefix to the output file references; default is no prefix
80 | path-prefix: ""
81 |
82 | # sorts results by: filepath, line and column
83 | sort-results: true
84 |
85 | # all available settings of specific linters
86 | linters-settings:
87 | gocyclo:
88 | # Minimal code complexity to report.
89 | # Default: 30 (but we recommend 10-20)
90 | min-complexity: 10
91 | linters:
92 | # please, do not use `enable-all`: it's deprecated and will be removed soon.
93 | # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
94 | # enable-all: true
95 | disable-all: true
96 | enable:
97 | - typecheck # 类型检查
98 | - bodyclose # body是否关闭
99 | - durationcheck # duration相乘检查
100 | - errcheck # 错误检查
101 | - exportloopref # 循环指针检查
102 | # - gofmt # gofmt
103 | # - gofumpt # gofumpt
104 | # - goimports # 导包顺序检查
105 | # - gosec # go安全检查
106 | - gosimple # 简化代码检查
107 | - govet
108 | - ineffassign # 变量是否未被使用
109 | - makezero # make非0长度切片
110 | - nilerr
111 | #- prealloc # 切片预分配
112 | #- revive 冗余代码检查
113 | - staticcheck # 静态检查
114 | - unparam # 无用的参数
115 | - unused # 变量是否使用
116 | - errchkjson # json err是否处理
117 | - gocyclo # 圈复杂度检查
118 | fast: false
119 |
120 | issues:
121 |
122 | # Show only new issues created after git revision `REV`
123 | # new-from-rev: REV
124 |
125 | # Show only new issues created in git patch with set file path.
126 | #new-from-patch: path/to/patch/file
127 |
128 | # Fix found issues (if it's supported by the linter)
129 | fix: false
130 |
131 |
132 |
--------------------------------------------------------------------------------