七牛云存储
晚来天欲雪,能饮一杯无?
1. 注册、登录
要使用七牛云的服务,首先需要注册成为会员。地https://portal.qiniu.com/signup址:
注册完成后就可以使用刚刚注册的邮箱和密码登录到七牛云。登录成功之后,还需要进行实名认证才能操作(创建存储空间)。
由于我们是个人使用,所以只需要申请个人认证。
1-1. 新建存储空间
要进行图片存储,我们需要在七牛云管理控制台新建存储空间。点击管理控制台首页对 象存储下的立即添加按钮,页面跳转到新建存储空间页面:

可以创建多个存储空间,各个存储空间是相互独立的。
创建成功之后,我们可以看见:
1-2. 查看存储空间信息
点击存储 空间名称可以查看当前存储空间的相关信息。
2. 开发者中心
可以通过七牛云提供的开发者中心学习如何操作七牛云服务,地 址:https://developer.qiniu.com/
七牛云提供了多种方式操作对象存储服务,本项目采用Java SDK方式,地 址:https://developer.qiniu.com/kodo/sdk/1239/java
使用Java SDK操作七牛云需要导入如下maven坐标:
3. 鉴权
Java SDK的所有的功能,都需要合法的授权。授权凭证的签算需要七牛账号下的一对有 效的Access Key和Secret Key,这对密钥可以在七牛云管理控制台的个人中心 (https://portal.qiniu.com/user/key)获得,如下图:
4. Java SDK操作七牛云
本章节我们就需要使用七牛云提供的Java SDK完成图片上传和删除,我们可以参考官方 提供的例子。
4-1. 上传文件
public class QiNiuYunTest {
@Test
public void test01() {
//构造一个带指定 Region 对象的配置类
Configuration cfg = new Configuration(Zone.zone2());
//...其他参数参考类注释
UploadManager uploadManager = new UploadManager(cfg);
//...生成上传凭证,然后准备上传
String accessKey = "zDx9DjpPq37DnvVo6Je_eBvhAnCh2hQFYg4PVxO7";
String secretKey = "Kg8YaOlgxwhrw6iMOvFeNTxLLzilpVAm3bc-uMBQ";
String bucket = "bianyihealth-01";//创建的存储空间的名称
//如果是Windows情况下,格式是 D:\qiniu\test.png
String localFilePath = "C:\Users\WEN\Pictures\Camera Roll\images\pic1.jpg";
//上传文件的名称,默认不指定key的情况下,以文件内容的hash值作为文件名
String key = null;
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket);
try {
Response response = uploadManager.put(localFilePath, key, upToken);
//解析上传成功的结果
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
System.out.println(putRet.key);
System.out.println(putRet.hash);
} catch (QiniuException ex) {
Response r = ex.response;
System.err.println(r.toString());
try {
System.err.println(r.bodyString());
} catch (QiniuException ex2) {
//ignore
}
}
}
}

控制台输出效果:

进去七牛云文件管理中心查询文件上传的效果:

点击详情,查看上传文件的详细信息。

将上图中文件链接复制就可以直接访问上传的图片。
4-2. 删除文件
@Test
public void test02() {
//构造一个带指定 Region 对象的配置类
Configuration cfg = new Configuration(Zone.zone0());
//...其他参数参考类注释
//...生成上传凭证,然后准备上传
String accessKey = "zDx9DjpPq37DnvVo6Je_eBvhAnCh2hQFYg4PVxO7";
String secretKey = "Kg8YaOlgxwhrw6iMOvFeNTxLLzilpVAm3bc-uMBQ";
//创建的存储空间的名称
String bucket = "bianyihealth-01";
String key = "pic1.jpg";
Auth auth = Auth.create(accessKey, secretKey);
BucketManager bucketManager = new BucketManager(auth, cfg);
try {
bucketManager.delete(bucket, key);
} catch (QiniuException ex) {
//如果遇到异常,说明删除失败
System.err.println(ex.code());
System.err.println(ex.response.toString());
}
}
5. 封装工具类
为了方便操作七牛云存储服务,我们可以将官方提供的案例简单改造成一个工具类,在 我们的项目中直接使用此工具类来操作就可以了。
将工具类复制到health-common子模块中。
public class QiniuUtils {
public static String accessKey = "zDx9DjpPq37DnvVo6Je_eBvhAnCh2hQFYg4PVxO7";
public static String secretKey = "Kg8YaOlgxwhrw6iMOvFeNTxLLzilpVAm3bc-uMBQ";
public static String bucket = "bianyihealth-01";
public static void upload2Qiniu(String filePath,String fileName){
//构造一个带指定Zone对象的配置类
Configuration cfg = new Configuration(Zone.zone2());
UploadManager uploadManager = new UploadManager(cfg);
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket);
try {
Response response = uploadManager.put(filePath, fileName, upToken);
//解析上传成功的结果
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
} catch (QiniuException ex) {
Response r = ex.response;
try {
System.err.println(r.bodyString());
} catch (QiniuException ex2) {
//ignore
}
}
}
//上传文件
public static void upload2Qiniu(byte[] bytes, String fileName){
//构造一个带指定Zone对象的配置类
Configuration cfg = new Configuration(Zone.zone2());
//...其他参数参考类注释
UploadManager uploadManager = new UploadManager(cfg);
//默认不指定key的情况下,以文件内容的hash值作为文件名
String key = fileName;
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket);
try {
Response response = uploadManager.put(bytes, key, upToken);
//解析上传成功的结果
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
System.out.println(putRet.key);
System.out.println(putRet.hash);
} catch (QiniuException ex) {
Response r = ex.response;
System.err.println(r.toString());
try {
System.err.println(r.bodyString());
} catch (QiniuException ex2) {
//ignore
}
}
}
//删除文件
public static void deleteFileFromQiniu(String fileName){
//构造一个带指定Zone对象的配置类
Configuration cfg = new Configuration(Zone.zone2());
String key = fileName;
Auth auth = Auth.create(accessKey, secretKey);
BucketManager bucketManager = new BucketManager(auth, cfg);
try {
bucketManager.delete(bucket, key);
} catch (QiniuException ex) {
//如果遇到异常,说明删除失败
System.err.println(ex.code());
System.err.println(ex.response.toString());
}
}
}
定义之后的文件目录如下:
6. 图片上传并预览
此处使用的是ElementUI提供的上传组件el-upload,提供了多种不同的上传效果,上传成功后可以进行预览。
实现步骤:
(1) 定义模型数据,用于后面上传文件的图片预览(回显):

(2) 定义文件上传组件


(3) 定义对应的函数
文件上传之前的函数:

文件上传成功后的函数:
(4) 创建SetmealController,接收上传的文件
public class SetmealController {
//文件上传
@RequestMapping("/upload")
public Result upload(@RequestParam("imgFile") MultipartFile imgFile){
System.out.println(imgFile);
//获取原始的文件上传的名字,主要用来截取文件后缀
String originalFilename = imgFile.getOriginalFilename();
int index = originalFilename.lastIndexOf(".");// pic1.jpg
String extention = originalFilename.substring(index);//.jpg
String fileName = UUID.randomUUID().toString()+extention;//文件上传之后的名称
try {
QiniuUtils.upload2Qiniu(imgFile.getBytes(),fileName);
} catch (IOException e) {
e.printStackTrace();
return new Result(false,MessageConstant.PIC_UPLOAD_FAIL);
}
return new Result(true,MessageConstant.PIC_UPLOAD_SUCCESS,fileName);
}
}
注意:别忘了在spring配置文件中配置文件上传组件:
在springmvc.xml中配置文件上传的配置
7. 完善文件上传
前面我们已经完成了文件上传,将图片存储在了七牛云服务器中。但是这个过程存在一个问题,就是如果用户只上传了图片而没有最终保存套餐信息到我们的数据库,这时我们上传的图片就变为了垃圾图片。对于这些垃圾图片我们需要定时清理来释放磁盘空间。这就需要我们能够区分出来哪些是垃圾图片,哪些不是垃圾图片。如何实现呢?
方案就是利用redis来保存图片名称,具体做法为:
1、当用户上传图片后,将图片名称保存到redis的一个Set集合中,例如集合名称为 setmealPicResources
2、当用户添加套餐后,将图片名称保存到redis的另一个Set集合中,例如集合名称为 setmealPicDbResources
3、计算setmealPicResources集合与setmealPicDbResources集合的差值,结果就是垃圾图片的名称集合,清理这些图片即可
本小节我们先来完成前面2个环节,第3个环节(清理图片环节)在后面会通过定时任务再实现。
实现步骤:
(1)在health_backend项目中提供Spring配置文件spring-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--Jedis连接池的相关配置-->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal">
<value>200</value>
</property>
<property name="maxIdle">
<value>50</value>
</property>
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="true"/>
</bean>
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg name="poolConfig" ref="jedisPoolConfig" />
<constructor-arg name="host" value="127.0.0.1" />
<constructor-arg name="port" value="6379" type="int" />
<constructor-arg name="timeout" value="30000" type="int" />
</bean>
</beans>
在springmvc.xml中加载spring-redis.xml配置文件
<!--加载redis配置文件-->
<import resource="spring-redis.xml"></import>
定义之后的目录结构如下:
(2) 在health_common工程中提供Redis常量类.
public class RedisConstant {
//套餐图片所有图片名称
public static final String SETMEAL_PIC_RESOURCES = "setmealPicResources";
//套餐图片保存在数据库中的图片名称
public static final String SETMEAL_PIC_DB_RESOURCES = "setmealPicDbResources";
}
定义之后的目录结构如下:
(3) 完善SetmealController,在文件上传成功后将图片名称保存到redis集合中.
(4)在health_service_provider项目中提供Spring配置文件spring-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--Jedis连接池的相关配置-->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal">
<value>200</value>
</property>
<property name="maxIdle">
<value>50</value>
</property>
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="true"/>
</bean>
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg name="poolConfig" ref="jedisPoolConfig" />
<constructor-arg name="host" value="127.0.0.1" />
<constructor-arg name="port" value="6379" type="int" />
<constructor-arg name="timeout" value="30000" type="int" />
</bean>
</beans>
(5) 完善SetmealServiceImpl服务类,在保存完成套餐信息后将图片名称存储到redis 集合中
(6) 测试