二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • 随写编年

  • 家人物语

  • 追忆青春

  • 父亲的朋友圈

  • 电影音乐

  • 效率工具

  • 博客相关

    • 原生文档

    • 个人折腾

      • 旧版博客上配置的一些小功能汇总
      • 解决wordpress新建页面没有模板的问题
      • wordpress整站迁移操作
      • 关于wordpress后台admin密码防爆破的一些操作
      • vuepress编译时报错TypeError _normalized undefined的解决
      • GitHub配置自动部署pages与服务器
      • Vuepress添加评论插件valine
      • Vuepress配置全文搜索插件fulltext-sarch
      • Vuepress添加侧边栏访问地图
      • vuepress文章内有模板语法时编译报错的解决
      • Vuepress配置评论插件为Artalk
      • 博客接入CDN的折腾-对阿里云七牛云蓝易云多吉云几家CDN使用评测
      • Vuepress添加首页轮播图与打赏按钮的配置
      • Vuepress配置rss订阅功能
      • Vuepress全文搜索插件vuepress-plugin-flexsearch-pro
      • Vuepress全文搜索终极版-algolia的开源实现meilisearch全接入指南
      • 基于多吉云存储+uPic构建个人图床的实践分享
        • 前言
        • 契机
        • 迁移
        • 结合uPic
          • 服务脚本
          • 自定义上传的配置
      • vuepress配置谷歌访问统计google-analytics
      • vuepress-vdoing主题配置LxgwWenKai字体
    • 转载推广

  • 闲言碎语
  • 博客相关
  • 个人折腾
二丫讲梵
2023-07-08
目录

基于多吉云存储+uPic构建个人图床的实践分享

文章发布较早,内容可能过时,阅读注意甄别。

# 前言

大约从 2021 年开始,我的博客上的图片,都是基于开源的图床程序来构筑的,这个开源的图床程序是:imgurl (opens new window),我平时的使用流程大概是这样:先在本地 Obsidian 将文章写就,然后再往博客的仓库 (opens new window)转移并二次发布,如果文章带有图片,则会通过工具对图片打水印,然后在浏览器访问图床,将图片上传到服务器,再将文章中的图片 URL 换掉。

以上就是我之前的个人图床使用经验,两年来使用还算是比较稳定可靠的。

# 契机

只是最近发现一些问题,就是偶尔程序的登陆状态出现问题,导致自己被关在门外,而无法正常上传图片,另外就是一直以来通过浏览器上传图片的方式也让我感到不方便,个人内心也早早就种下想要换掉图床程序的种子。

我曾经写过一篇有关于图床的文章:与图床相关的各种体验实践 (opens new window),那时候对云存储以及 CDN 这些都还不够了解,所以始终没有把云存储利用起来。

另外一个契机在于:我于今年三月份将博客 CDN 切换到多吉云,并发布了一篇国内 CDN 的评测文章:博客接入CDN的折腾-对阿里云七牛云蓝易云多吉云几家CDN使用评测 (opens new window),在文章里放了个人的邀请链接,并没有想过以此获得什么。然而就在前几天,忽然收到短信,发现上个月有人通过我的链接注册了多吉云,并且购买了云存储的资源包,使得我也获得了两百多的收益 (感谢这位不知名的朋友),这令我感到非常惊喜,也进一步激发我尝试把图床接入到多吉云的云存储。

因此,在介绍我的迁移以及优雅实践之前,再一次分享一下我的邀请链接:

注册多吉云

🫡 通过我的邀请链接注册多吉云。

- name: 注册多吉云
  desc: 🫡 通过我的邀请链接注册多吉云。
  link: https://www.dogecloud.com/?iuid=5485
  bgColor: '#6393e2'
  textColor: '#242A38'
1
2
3
4
5

如果你的确有云存储与CDN方面的使用需要,可通过我的邀请链接注册多吉云,那么你第一年的消费我将能拿到一些奖励。

多吉云并没有给我任何广告费用,但我十分愿意推荐多吉云给大家,因为这也是折腾了五六年博客的我经过各种体验评测之后,认为对于个人博客使用者最友好,性价比最好,稳定性也不错的一家服务商。

# 迁移

先说一下基于多吉云云存储建立图床的方式以及访问流程,在云存储里边,创建一个独立的存储空间,然后给这个存储空间绑定一个域名,就可以直接通过这个域名来访问存储空间中的文件了。

因为我之前个人图床的图片都是在服务器本地,因此只需要把图片全量转移到多吉云存储里边。然后路径与原来保持一致,然后将原来图床对应的域名解析改到多吉云的CDN地址即可。

这样只会产生一个存储的费用,以及CDN的流量费用,因此目前我个人博客图片存储量并不大,因此存储的费用每个月几乎可以忽略不计,而流量费用则基本上等同于原来图床域名接入CDN的流量费用,因此整体算来,这还是一个性价比比较高的一次迁移。

# 结合uPic

因为使用云存储了,再像原来每次上传图片还要打开浏览器去上传就比较low了,因此我就打算折腾一下,通过原来一直在用的 uPic (opens new window) 图床上传软件来进行上传的动作的触发。

于是写了一个脚本,起了一个接受图片上传请求,并把图片上传到云存储的服务,通过docker把服务守护在本地,然后借助uPic的自定义上传能力,将整个链路打通。

# 服务脚本

这里把服务脚本简单放在这里,有需要的同学可自行打包部署,当然,如果需要的同学比较多,而且不方便打包,也可以在评论区留言,如果要的人多,我可以花点时间折腾一下,把项目开源,直接做成一键部署的成品。

package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/gin-gonic/gin"
)

var (
	// 这里替换为你的多吉云永久 AccessKey 和 SecretKey,可在用户中心 - 密钥管理中查看
	// 请勿在客户端暴露 AccessKey 和 SecretKey,那样恶意用户将获得账号完全控制权
	AccessKey = "xxxxxx"
	SecretKey = "xxxxxxxxxxxxxxxxxxxx"
)

func main() {
	// 创建gin实例
	r := gin.Default()

	// 设置路由和处理函数
	r.POST("/upload", func(c *gin.Context) {
		file, err := c.FormFile("source")
		if err != nil {
			c.JSON(400, gin.H{
				"error": err.Error(),
			})
			return
		}
		src, err := file.Open()
		if err != nil {
			c.JSON(500, gin.H{
				"error": err.Error(),
			})
		}
		defer src.Close()
		fpath := getFilePath(file.Filename)
		_, err = GetS3Client().PutObject(&s3.PutObjectInput{
			Bucket: aws.String("s-sh-5485-wiki-images-1258813047"),
			Key:    aws.String(fpath),
			Body:   src,
		})
		if err != nil {
			fmt.Printf("文件上传出错,请检查: %v\n", err)
			c.JSON(500, gin.H{
				"error": err.Error(),
			})
		}

		c.JSON(200, gin.H{
			"code":    200,
			"url":     fpath,
			"message": "File uploaded successfully",
		})
	})

	// 启动服务器
	r.Run(":666")
}

func getFilePath(name string) string {
	formattedTime := time.Unix(time.Now().Unix(), 0).Format("2006/01")
	return fmt.Sprintf("imgs/%s/%d%s", formattedTime, time.Now().UnixMilli(), filepath.Ext(name))
}

func DogeCloudAPI(apiPath string, data map[string]interface{}, jsonMode bool) (ret map[string]interface{}) {
	body := ""
	mime := ""
	if jsonMode {
		_body, err := json.Marshal(data)
		if err != nil {
			log.Fatalln(err)
		}
		body = string(_body)
		mime = "application/json"
	} else {
		values := url.Values{}
		for k, v := range data {
			values.Set(k, v.(string))
		}
		body = values.Encode()
		mime = "application/x-www-form-urlencoded"
	}

	signStr := apiPath + "\n" + body
	hmacObj := hmac.New(sha1.New, []byte(SecretKey))
	hmacObj.Write([]byte(signStr))
	sign := hex.EncodeToString(hmacObj.Sum(nil))
	Authorization := "TOKEN " + AccessKey + ":" + sign

	req, err := http.NewRequest("POST", "https://api.dogecloud.com"+apiPath, strings.NewReader(body))
	if err != nil {
		fmt.Printf("http request failed : %v\n", err)
	}
	req.Header.Add("Content-Type", mime)
	req.Header.Add("Authorization", Authorization)
	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatalln(err)
	} // 网络错误
	defer resp.Body.Close()
	r, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("read body failed : %v\n", err)
	}
	json.Unmarshal([]byte(r), &ret)

	return
}

func GetS3Client() *s3.S3 {
	prof := make(map[string]interface{})
	prof["channel"] = "OSS_FULL"
	prof["scopes"] = [1]string{"*"}
	r := DogeCloudAPI("/auth/tmp_token.json", prof, true)
	data := r["data"].(map[string]interface{})
	creds := data["Credentials"].(map[string]interface{})
	s3Config := &aws.Config{
		Credentials: credentials.NewStaticCredentials(creds["accessKeyId"].(string), creds["secretAccessKey"].(string), creds["sessionToken"].(string)),
		Region:      aws.String("automatic"),
		Endpoint:    aws.String("https://cos.ap-shanghai.myqcloud.com"), // 修改为多吉云控制台存储空间 SDK 参数中的 s3Endpoint
	}
	newSession, err := session.NewSession(s3Config)
	if err != nil {
		panic(fmt.Errorf("初始化s3客户端失败: %v\n", err))
	}
	return s3.New(newSession)
}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

我把这个服务构建为一个镜像,然后每次电脑的 docker 启动之后就会自动运行:

docker run -itd --name doge -p 666:666 doge
1

此时本地就会有个 666 端口的监听。

# 自定义上传的配置

这里直接通过一张截图来展现配置的详细内容:

1688811635060

  1. 首先新建一个配置,选择自定义类型。
  2. 如果你也是在本地运行,则可以填写为: http://localhost:666/upload
  3. 该接口为 POST 请求。
  4. 文件字段名为:source,写死不要更改。
  5. 这里点击其他字段,要在 header 中增加一下请求类型,key:content-type,value:multipart/form-data; charset=utf-8;
  6. 此处为取文件的存放路径,固定为 ["url"],不要更改。
  7. 最后域名则是你绑定在多吉云中的域名。

当我们点击验证,如果返回了上传成功的提示,则说明整个链路通畅了,uPic 也会把图片的路径通过域名与路径拼接起来自动放到粘贴板。

这样就可以愉快地通过 uPic 来高效的完成与图床的交互了。

如果你也想这样折腾,那就赶快行动起来吧,配置过程中有任何问题,都可以在评论区留言,我看到之后一定也会知无不言。

微信 支付宝
上次更新: 2024/04/25, 22:08:24
Vuepress全文搜索终极版-algolia的开源实现meilisearch全接入指南
vuepress配置谷歌访问统计google-analytics

← Vuepress全文搜索终极版-algolia的开源实现meilisearch全接入指南 vuepress配置谷歌访问统计google-analytics→

最近更新
01
记录二五年五一之短暂回归家庭
05-09
02
学习周刊-总第210期-2025年第19周
05-09
03
学习周刊-总第209期-2025年第18周
05-03
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式