← 返回文章

博客文章上传图片

博客终于可以直接上传图片了

前言

成功了,博客内容中可以插入图片了,虽然是很简单的一个功能,但是也耗费了我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
}

END