使用腾讯云OSS作为个人网站文件处理的存储库
在我之前的网站中,文件存储是自己写的,文件保存到服务端本地。这样的弊端很多,占用服务器空间、占用服务器流量、服务器带宽较低且不保证全球访问、难以很想拓展等。为了解决这个问题,我决定把存储单独抽离出来,由腾讯云oss完成。
# 整体介绍
下载文件:
上传文件:
核心就是一个腾讯云的预签名URL。
# 预签名URL生成
初始化客户端:
func NewCosClient(conf *config.Config) *cos.Client {
u, _ := url.Parse(conf.CosConfig.Endpoint)
b := &cos.BaseURL{BucketURL: u}
client := cos.NewClient(b, &http.Client{
Transport: &cos.AuthorizationTransport{
SecretID: conf.CosConfig.Ak, // 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
SecretKey: conf.CosConfig.Sk, // 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
},
})
return client
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
其中endpoint我使用了全球加速域名,没有用自定义域名,缺点是不能下载部分特殊格式(例如apk)的文件。 ak和sk建议用子账号的,仅给这个子账号oss的读写权限。
生成预签名url:
func (s *FileService) GetSignedUrl(ctx context.Context, method, name string) (string, error) {
signedURL, err := s.CosClient.Object.GetPresignedURL(ctx, method, name, s.Config.CosConfig.Ak, s.Config.CosConfig.Sk, time.Hour, nil)
if err != nil {
return "", err
}
return signedURL.String(), err
}
1
2
3
4
5
6
7
2
3
4
5
6
7
其中,如果是上传文件,则method使用PUT(不可以是POST),下载则是GET。这里的签名过期时间用了一小时,因为我的文件都是很小的文件,一小时足够了。如果你的文件较大,需要调大一些,不然下载到一半就会断开连接了。
name是文件的名称,可以带有""表示文件夹,例如"2025\2\18\xxx.jpg"
# 下载文件名问题
由于后端保存的时候不是按照实际文件名保存的,而是用的uuid,所以前端直接访问url会出现文件名是uuid的情况。
解决方法有一个就是前端使用blob,先把文件下载到一个blob对象,在用实际文件名下载到本地。不过这样有一个问题就是占用内存,而且看不到实际的下载进度。
另外一个办法是查阅腾讯云OSS文档 (opens new window)看到的。可以通过response-content-disposition请求参数来设置响应中的 Content-Disposition 头部的值。因此对签名函数进行了改进:
func (s *FileService) GetSignedUrl(ctx context.Context, method, ossPath, name string) (string, error) {
opt := &cos.PresignedURLOptions{
Query: &url.Values{},
Header: &http.Header{},
}
if method == http.MethodGet {
opt.Query.Add("response-content-disposition", fmt.Sprintf("attachment; filename=%s", name))
// 重点是这一行,通过设置返回的Content-Disposition响应头来达到修改下载的文件名的目的。
}
signedURL, err := s.CosClient.Object.GetPresignedURL(ctx, method, ossPath, s.Config.CosConfig.Ak, s.Config.CosConfig.Sk, time.Hour, opt)
if err != nil {
return "", err
}
return signedURL.String(), err
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
编辑 (opens new window)
上次更新: 2025/03/29, 14:50:20