微信小程序使用七牛云存储
导读
最近在做一个微信小程序项目,项目中涉及到大量图片的存储,由于之前的项目用到了七牛云存储服务,所以也打算在此项目中选用七牛云作为资源站点使用。
七牛云的存储方式为:服务端转储,客户端直传,客户端token上传;
本文主要讲解客户端token上传
开发前的准备
七牛云配置
首先是创建七牛云账号,实名认证后,在控制台中开通存储服务。
首次使用七牛云服务的使用,会生成一对秘钥,后续需要使用此秘钥生成token。
新创建的存储空间会有一个月的免费域名使用,到期后需要使用自己的域名才能访问存储空间的内容,域名需要备案,备案时间为一到两周,如果是还未申请域名,可以提前准备。
这里使用七牛云免费提供的域名进行操作,自定义域名配置不做赘述。
微信小程序测试号配置
申请小程序测试号,配置服务器域名
java配置-spring-boot
application.yml配置
qiniu:
ak: 七牛云AK
sk: 七牛云SK
domain: 外链域名
bucket: 空间名称
pom-七牛云SDK
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.2.29</version>
</dependency>
正式开始
java端代码
七牛云工具类编写
/**
* 工具类中一些常量可能会在别的地方用到,所以定义为静态
*/
@Slf4j
@Component
public class QiniuUtil{
public static String bucket;
@Value("${qiniu.bucket}")
public void setBucket(String bucket) {
QiniuUtil.bucket = bucket;
}
public static String domain;
@Value("${qiniu.domain}")
public void setDomain(String domain) {
QiniuUtil.domain = domain;
}
//构造一个带指定Zone对象的配置类
public static Configuration configuration = new Configuration(Region.huanan());
public static UploadManager uploadManager = new UploadManager(configuration);
public static BucketManager bucketManager;
public static Auth auth;
@Autowired
public void setAuth(@Value("${qiniu.ak}")String accessKey,
@Value("${qiniu.sk}") String secretKey){
QiniuUtil.auth = Auth.create(accessKey, secretKey);
QiniuUtil.bucketManager = new BucketManager(auth, configuration);
}
// 获取一个不会过期的,限定空间名称的token
public static String getToken(){
return auth.uploadToken(bucket);
}
// 获取一个限定时间,限定空间,限定文件名称的token
public static String getToken(String bucket,String key){
return auth.uploadToken(bucket,key,1800,new StringMap());
}
// 获取限定时间,限定空间,限定前缀的的token
public static String getPrefixToken(String prefix){
return getPrefixToken(bucket,prefix);
}
public static String getPrefixToken(String bucket,String prefix){
StringMap putPolicy = new StringMap();
// 限定空间及前缀
putPolicy.put("scope",bucket+":"+prefix);
// 是否限定前缀-1
putPolicy.put("isPrefixalScope",1);
// 设置当前时间延后半小时过期
putPolicy.put("deadline",System.currentTimeMillis()/1000+1800);
return auth.uploadToken(bucket, prefix, 1800, putPolicy);
}
}
响应类编写
@Data
public class HttpResult<T> implements Serializable{
@ApiModelProperty("返回代码")
private Integer code;
@ApiModelProperty("消息")
private String msg;
@ApiModelProperty("返回数据")
private T data;
public HttpResult(){ }
public HttpResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public HttpResult(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static<T> HttpResult success(){
return new HttpResult<>(200,"操作成功");
}
// 成功,仅返回提示消息
public static<T> HttpResult success(String msg){
return new HttpResult<>(200,msg);
}
// 成功,返回数据与消息
public static<T> HttpResult success(String msg , T data){
return new HttpResult<>(200,msg,data);
}
// 成功,仅返回数据
public static<T> HttpResult success(T data){
return new HttpResult<>(200,"操作成功",data);
}
}
七牛云响应结果
@Data
@Accessors(chain = true)
public class QiniuResult {
private String uptoken;
// 返回指定前缀
private String prefixKey;
public QiniuResult(String uptoken) {
this.uptoken = uptoken;
}
public QiniuResult(String uptoken,String prefixKey) {
this.uptoken = uptoken;
this.prefixKey = prefixKey;
}
}
获取token的控制器
因为多是批量上传,为了防止上传中断,导致无效图片占用空间,这里利用rabbit的消息过期机制,进行延时删除操作。
能完成延时功能的技术有很多,这里仅是为了练习rabbitMQ的使用。
@RestController
@RequestMapping("/util")
public class UtilController {
@Autowired
private RedisUtil redisUtil;
@Autowired
private RabbitUtil rabbitUtil;
@GetMapping("/getUpToken")
@ApiOperation("获取七牛云upToken")
public HttpResult getQiniuToken(HttpServletRequest request,
@ApiParam("详情图片数量")
@RequestParam(name = "size", defaultValue = "0") Integer size,
@ApiParam("图片类型") @RequestParam(name = "type") String type) {
// 未收到图片数量
if (size == 0) throw new CommonBaseException(CommonBaseErrorCode.NO_FILE_TO_UPLOAD);
// 设置消息过期时间
MessageProperties messageProperties = new MessageProperties();
messageProperties.setExpiration(RabbitConstant.imgDelayTime.toString());
// 生成商品图片前缀
String uuId16 = request.getAttribute("userId")+"/"+OtherUtil.generateShortUuid();
// 创建消息并发送
Message message = new Message((size + "-" + uuId16).getBytes(), messageProperties);
rabbitUtil.sendMessage(RabbitConstant.delayExchange, RabbitConstant.delayTempKey, message);
redisUtil.set(Constant.PRODUCT_IMG_KEY+uuId16,"1",RabbitConstant.imgDelayTime+300);
// 返回uptoken和商品前缀,token按类型做前缀分类
return HttpResult.success(new QiniuResult(QiniuUtil.getPrefixToken(type+"/"+uuId16), uuId16));
}
小程序代码
七牛云工具类
本工具类基于七牛云社区小程序SDK改造而来
// created by gpake
(function () {
var config = {
// 文件上传路径,根据七牛云所处地区设置,
uploadURL: 'https://up-z2.qiniup.com',
// 自身空间CDN加速域名,用作返回文件路径时,拼接展示
domain: '',
// 上传凭证
upToken: '',
// 上传凭证获取地址
upTokenURL: '',
// 上传凭证获取函数
upTokenFunction: null
}
module.exports = {
config: config,
init: init,
doUpload: doUpload,
refreshUpToken: refreshUpToken,
uploadURLFromRegionCode:uploadURLFromRegionCode
}
// 在整个程序生命周期中,只需要 init 一次即可
// 如果需要变更参数,再调用 init 即可
function init(options) {
config = {
uploadURL: '',
domain: '',
upToken: '',
upTokenURL: '',
upTokenFunction: null
};
updateConfigWithOptions(options);
}
// 利用自定义属性更新七牛云配置
function updateConfigWithOptions(options) {
if (options.uploadURL) {
config.uploadURL = options.uploadURL;
} else if(options.region){
config.uploadURL=uploadURLFromRegionCode(options.region)
}else{
console.error('qiniu uploader need uploadURL');
}
if (options.upToken) {
config.upToken = options.upToken;
} else if (options.uptokenURL) {
config.upTokenURL = options.uptokenURL;
} else if (options.uptokenFunc) {
config.upTokenFunction = options.uptokenFunc;
}
if (options.domain) {
config.domain = options.domain;
}
}
// 使用配置中,获取upToken的URL获取token
function getUpToken(callback) {
wx.request({
url: config.upTokenURL,
success: function (res) {
var token = res.data.uptoken;
config.upToken = token;
if (callback) {
callback();
}
},
fail: function (error) {
console.error(error);
}
})
}
// 刷新token
function refreshUpToken(options) {
config.upToken = '';
if (options.upToken) {
config.upToken = options.upToken;
} else if (options.callback) {
if (config.uptokenURL) {
getUpToken(callback);
} else if (config.upTokenFunction) {
}
} else if (config.upTokenFunction) {
config.upToken = config.upTokenFunction(params);
}
if(!config.upToken || config.upToken===''){
console.error('qiniu uploadToken is losed');
}
}
// 执行上传操作,文件路径
function doUpload(filePath, options) {
//封装promise
return new Promise((resolve, reject) => {
// 过滤token
if(!config.upToken || config.upToken===''){
reject('qiniu uploadToken is losed')
}
//过滤文件名
let fileName;
if (options && options.key) {
fileName = options.key;
} else {
fileName = filePath.split('//')[1];
}
// 封装token和文件名
let formData = {
'token': config.upToken,
'key': fileName
};
// 使用微信上传API,上传到指定地域的域名
wx.uploadFile({
url: config.uploadURL,
filePath: filePath,
name: 'file',
formData: formData,
success: function (res) {
// 如果状态码为200 则上传成功,解析参数后返回
if(res.statusCode==200){
let dataObject = JSON.parse(res.data);
dataObject.imageURL = config.domain + dataObject.key;
resolve(dataObject);
}else{
//不然则上传失败,抛出结果
reject(res)
}
},
fail: function (error) {
reject(error)
}
})
})
}
// 根据地域缩写获取指定地域上传域名
function uploadURLFromRegionCode(code) {
var uploadURL = null;
switch (code) {
case 'ECN'://华东地址
uploadURL = "https://upload.qiniup.com";
break;
case 'NCN'://华北地址
uploadURL = 'https://upload-z1.qiniup.com';
break;
case 'SCN'://华南地址
uploadURL = 'https://upload-z2.qiniup.com';
break;
case 'NA'://北美地址
uploadURL = 'https://upload-na0.qiniup.com';
break;
default:
console.error('please make the region is with one of [ECN, SCN, NCN, NA]');
}
return uploadURL;
}
})();
工具类的调用
const qiniuUploader = require("../../../utils/qiniuUploader");
function initQiniu(upToken) {
var options = {
upToken: upToken,
uploadURL: 'https://up-z2.qiniup.com',
domain:'http://static.***.***.***/'
};
qiniuUploader.init(options);
}
Page({
upload: async function(){
// 设置类型未商品
let type='product';
//获取指定前缀的token,这里封装了个request工具类,做同步请求。
let qiniuRes = await request.request("get","/util/getUpToken",{type:type,size:that.data.descImgs.length});
// 拿到后台返回的key
let key=qiniuRes.data.prefixKey;
// 拼接商品前缀
let prefixKey= type+'/'+key;
// 使用token初始化七牛云
initQiniu(qiniuRes.data.uptoken);
// 上传商品封面
let cover=that.data.coverImg;
let coverRes = await qiniuUploader.doUpload(cover,{key:prefixKey+"/cover"+cover.substr(cover.lastIndexOf("."))});
// 遍历上传商品详情
let descs = that.data.descImgs;
// 这里使用for-i循环,因为forEach会使上传方法变异步,如果没有同步需求,可以使用forEach方法
for(let i =0,len=descs.length;i<len;i++){
let descRes = await qiniuUploader.doUpload(descs[i],{key:prefixKey+"/desc_"+i+descs[i].substr(descs[i].lastIndexOf("."))});
}
}
})
总结
这里只是简单的将整个七牛云token上传流程进行梳理,关键的只是配置问题,其余代码各位可以根据自身具体需求,修改其中逻辑。
查阅资料
七牛云
七牛云token配置属性.
七牛云微信小程序SDK文档-github.
七牛云各地域上传域名.
七牛云JAVA-SDK官方文档.