博客文章上传图片
博客终于可以直接上传图片了
前言
成功了,博客内容中可以插入图片了,虽然是很简单的一个功能,但是也耗费了我7个晚上的时间。
归根结底,还是 golang 语言的不熟悉,它对 io.Reader 的各种实现,还是很陌生。之后还是得再看看。
不管过程如何,结果是好的,在这几记录一下实现过程。
前端逻辑
前端 react 中,使用的是 rich-markdown-editor 这个组件,它本身就提供了插入图片的函数( uploadImage ),我们只需要实现此函数即可。
// 需要实现的函数
uploadImage={async file => {
const result = await uploadImageFromPost(file);
return result.data;
}}
在这里,我们通过实现一个 uploadImageFromPost 函数来实现图片的上传,并返回图片的 url 地址。
- 参数是一个 blob 类型的 file 文件。
- 通过使用一个 blobToDataURL 函数将图片文件转成 base64 的字符串。
- 将 base64 字符串上传到后端,由后端做一定的处理之后,将图片存储的 url 返回回来。
在这其中,对 js 的Promise 的使用有了更深一点的理解了, 它不管这个函数什么时候能执行完,它只管什么时候能接收到 reslove 或者 reject 传过来的值。好有意思。
/**
* 上传图片到服务器
*/
function uploadImageFromPost(file){
return new Promise((resolve, reject) => {
blobToDataURL(file, function(dataurl){
var data = {imgBase64:dataurl}
http("post",'/tuchuang/uploadImageFromPost',data).then(res => {
resolve(res);
},error => {
console.log("网络异常~",error);
reject(error)
})
});
})
}
function blobToDataURL(blob, callback) {
var a = new FileReader();
a.onload = function (e) { callback(e.target.result); }
a.readAsDataURL(blob);
}
前端的工作基本就做完了,很简单。
后端,我对接的是 七牛云 的图片服务器,它提供了 golang 的sdk,也提供了上传文件的事例。但我总不能在后端存储一个临时图片,再将图片上传 七牛云,再将临时图片删除吧,有点脱了裤子放屁的感觉,所以这种方法完全不行,要以 buffer 的形式在后端流转。就因为这么一个简单的事,拖了我好几天。唉~
后端逻辑
然后,简单介绍一下我后台的逻辑。
- 连接 七牛云 需要从它的服务器获取一个 token,它有一个 expires 过期时间。所以我使用了 单例模式 来获取 token。
func getToken() string {
accessKey := "*********" // 需要从七牛云 console 获取
secretKey := "*****-*****" // 需要从七牛云 console 获取
bucket := "****" // 七牛云自己定义的 namespace 的名字
putPolicy := storage.PutPolicy{
Scope: bucket,
}
mac := qbox.NewMac(accessKey, secretKey)
upToken := putPolicy.UploadToken(mac)
// 返回的数据:{"hash":"Ftgm-CkWePC9fzMBTRNmPMhGBcSV","key":"qiniu.jpg"}
return upToken
}
type SingleToen struct {
token string // token 信息
expires uint32 // 截止时间
}
var token_instance *SingleToen
func getToken_instance() *SingleToen {
if token_instance == nil {
token_instance = &SingleToen{
token: getToken(),
expires: time.Now().Unix(),
}
return token_instance
}
// 判断 token 是否过期,过期则需重新申请, 默认的过期时间是 3600 s
if time.Now().Unix()-token_instance.expires >= 3600 {
token_instance = &SingleToen{
token: getToken(),
expires: time.Now().Unix(),
}
return token_instance
}
return token_instance
}
通过以上的函数,无需每次上传图片都去获取 token,而是检查 token 是否过期,如果过期了,再重新申请。
再然后,就是将图片的 base64 字符串上传到 七牛云,重点就是两步,其他的都是 七牛云 本身上传文件需要的各种参数。
- 第一步,将 图片的base64 字符串转换成 字节数组 :base64.StdEncoding.DecodeString
- 第二步,将字节数组转化成 Buffer 类型。
/**
UploadImgByUrl2 上传图片到七牛云,参数如下:
data 是要上传的图片的 base64 字符串;
key 是要上传的文件访问路径;
*/
func UploadImgByUrl2(data string, key string) error {
ddd, _ := base64.StdEncoding.DecodeString(data) //成图片文件并把文件写入到buffer
_ = ioutil.WriteFile("./output.jpg", ddd, 0666) //buffer输出到jpg文件中(不做处理,直接写到文件)
upToken := getToken_instance().token
cfg := storage.Config{}
// 空间对应的机房
cfg.Zone = &storage.ZoneHuanan
// 是否使用https域名
cfg.UseHTTPS = true
// 上传是否使用CDN上传加速
cfg.UseCdnDomains = false
formUploader := storage.NewFormUploader(&cfg)
ret := storage.PutRet{}
dataLen := int64(len(ddd))
err := formUploader.Put(context.Background(), &ret, upToken, key, bytes.NewBuffer(ddd), dataLen, nil)
if err != nil {
fmt.Println(err)
return err
}
fmt.Println(ret.Key, ret.Hash)
return nil
}