一、图片存储方案
1.1 常见图片存储方案

实际项目中会拆分不同功能服务器,提升系统运行效率,图片存储常用三种方案:
- nginx 搭建图片服务器
- 分布式文件存储系统(fastdfs、hdfs)
- 云存储(阿里云 oss、七牛云)
本文选用七牛云对象存储,接入简单、cdn 加速快、适合中小项目快速落地。
1.2 七牛云使用流程
1.2.1 注册与实名认证
- 访问七牛云官网:https://www.qiniu.com/
- 注册账号并完成个人实名认证(创建存储空间必须认证)
1.2.2 新建存储空间
控制台进入对象存储 kodo

- 创建存储空间(bucket):
- 名称:3~63 位小写字母 / 数字 / 短横线
- 存储区域:华东 / 华北 / 华南等
- 访问控制:公开 / 私有(图片通常设为公开)

- 创建成功后可在文件管理查看上传资源。
1.2.3 获取 ak/sk 密钥
- 进入个人中心 → 密钥管理
- 复制 accesskey 和 secretkey(java sdk 鉴权使用)

1.3 java sdk 接入七牛云

1.3.1 引入 maven 依赖
<!-- 七牛云sdk -->
<dependency>
<groupid>com.qiniu</groupid>
<artifactid>qiniu-java-sdk</artifactid>
<version>7.7.0</version>
</dependency>1.3.2 封装七牛云工具类
将上传、删除封装为工具类,放入公共模块(ican-common):
public class qiniuutils {
public static string accesskey = "你的ak";
public static string secretkey = "你的sk";
public static string bucket = "你的存储空间名";
// 文件路径上传
public static void upload2qiniu(string filepath,string filename){
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) {
ex.printstacktrace();
}
}
// 字节数组上传
public static void upload2qiniu(byte[] bytes, string filename){
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(bytes, filename, uptoken);
defaultputret putret = new gson().fromjson(response.bodystring(), defaultputret.class);
} catch (qiniuexception ex) {
ex.printstacktrace();
}
}
// 删除文件
public static void deletefilefromqiniu(string filename){
configuration cfg = new configuration(zone.zone2());
auth auth = auth.create(accesskey, secretkey);
bucketmanager bucketmanager = new bucketmanager(auth, cfg);
try {
bucketmanager.delete(bucket, filename);
} catch (qiniuexception ex) {
system.err.println(ex.code());
}
}
}
二、新增套餐功能(图片上传 + 多对多关联)
2.1 需求说明
套餐是检查组的集合,套餐与检查组为多对多关系,需中间表t_setmeal_checkgroup关联。新增套餐需录入:
- 基本信息(编码、名称、价格、图片等)
- 关联检查组
2.2 前端实现(vue+elementui)
2.2.1 弹出新增窗口
点击新建按钮,清空表单并展示弹窗,同时加载所有检查组:
handlecreate(){
this.resetform();
this.dialogformvisible = true;
// 查询所有检查组
axios.get("/checkgroup/findall.do").then((res)=>{
if(res.data.flag){
this.tabledata = res.data.data;
}else{
this.$message.error(res.data.message);
}
});
}2.2.2 图片上传与预览
使用el-upload组件,限制 jpg 格式、大小≤2mb:
<el-upload class="avatar-uploader" action="/setmeal/upload.do" :auto-upload="autoupload" name="imgfile" :show-file-list="false" :on-success="handleavatarsuccess" :before-upload="beforeavatarupload"> <img v-if="imageurl" :src="imageurl" class="avatar"> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload>
上传前校验:
beforeavatarupload(file){
const isjpg = file.type === 'image/jpeg';
const islt2m = file.size / 1024 / 1024 < 2;
if(!isjpg) this.$message.error("只能上传jpg格式!");
if(!islt2m) this.$message.error("图片大小不能超过2mb!");
return isjpg && islt2m;
}2.3 后端实现
2.3.1 图片上传接口
@controller
@requestmapping("/setmeal")
public class setmealcontroller {
@autowired
private jedispool jedispool;
// 图片上传
@requestmapping("/upload")
@responsebody
public result upload(@requestparam("imgfile") multipartfile imgfile){
try {
string originalfilename = imgfile.getoriginalfilename();
string extention = originalfilename.substring(originalfilename.lastindexof("."));
string filename = uuid.randomuuid().tostring() + extention;
// 上传七牛云
qiniuutils.upload2qiniu(imgfile.getbytes(),filename);
// 存入redis(所有上传图片)
jedispool.getresource().sadd(redisconstant.setmeal_pic_resources,filename);
return new result(true, messageconstant.pic_upload_success,filename);
} catch (exception e) {
e.printstacktrace();
return new result(false, messageconstant.pic_upload_fail);
}
}
}
2.3.2 新增套餐(事务 + 多对多关联)
@service
@transactional
public class setmealserviceimpl implements setmealservice {
@autowired
private setmealdao setmealdao;
@override
public void add(setmeal setmeal, integer[] checkgroupids) {
// 新增套餐基本信息
setmealdao.add(setmeal);
integer setmealid = setmeal.getid();
// 设置套餐与检查组关联
this.setsetmealandcheckgroup(setmealid,checkgroupids);
}
// 维护多对多关系
private void setsetmealandcheckgroup(integer setmealid, integer[] checkgroupids) {
if(checkgroupids != null && checkgroupids.length > 0){
for (integer checkgroupid : checkgroupids) {
map<string,integer> map = new hashmap<>();
map.put("setmealid",setmealid);
map.put("checkgroupid",checkgroupid);
setmealdao.setsetmealandcheckgroup(map);
}
}
}
}
2.3.3 保存套餐后同步 redis
@requestmapping("/add")
@responsebody
public result add(@requestbody setmeal setmeal, integer[] checkgroupids){
try {
setmealservice.add(setmeal,checkgroupids);
// 存入redis(已保存到数据库的图片)
jedispool.getresource().sadd(redisconstant.setmeal_pic_db_resources,setmeal.getimg());
return new result(true, messageconstant.add_setmeal_success);
}catch (exception e){
e.printstacktrace();
return new result(false, messageconstant.add_setmeal_fail);
}
}
三、定时任务组件 quartz(重点)
3.1 quartz 核心概念
- job:要执行的任务(清理图片)
- trigger:触发器(定义执行时间)
- scheduler:调度器(绑定任务与触发器)

3.2 cron 表达式
格式:秒 分 时 日 月 周 年(年可省略)常用示例:
0/1 * * * * ?:每秒执行0 0 2 * * ?:每天凌晨 2 点执行0 0 0/1 * * ?:每小时执行


示例:

前面介绍了cron表达式,但是自己编写表达式还是有一些困难的,我们可以借助一些 cron表达式在线生成器来根据我们的需求生成表达式即可。
http://cron.qqe2.com/
3.3 springboot 整合 quartz
3.3.1 引入依赖
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-quartz</artifactid>
</dependency>3.3.2 配置类
@configuration
public class quartzconfig {
@bean //job:干什么事
public methodinvokingjobdetailfactorybean jobdetail(clearimgjob clearimgjob){
methodinvokingjobdetailfactorybean bean = new methodinvokingjobdetailfactorybean();
bean.settargetobject(clearimgjob);
bean.settargetmethod("clearimg");
return bean;
}
@bean //trigger:什么时候
public crontriggerfactorybean trigger(methodinvokingjobdetailfactorybean jobdetail){
crontriggerfactorybean bean = new crontriggerfactorybean();
bean.setcronexpression("0 0 2 * * ?"); // 每天凌晨2点
bean.setjobdetail(jobdetail.getobject());
return bean;
}
@bean //scheduler:什么时候干什么事
public schedulerfactorybean scheduler(crontriggerfactorybean trigger){
schedulerfactorybean bean = new schedulerfactorybean();
bean.settriggers(trigger.getobject());
return bean;
}
}
四、定时清理垃圾图片(核心)
4.1 垃圾图片产生原因
用户上传图片后未提交套餐,图片存于七牛云但无数据库记录,成为垃圾文件。
4.2 清理思路
- redis 集合 a:
setmealpicresources(所有上传图片) - redis 集合 b:
setmealpicdbresources(已保存数据库图片) - 求差集:a - b = 垃圾图片
- 定时删除七牛云 + redis 中的垃圾文件
4.3 清理任务实现
@component
public class clearimgjob {
@autowired
private jedispool jedispool;
public void clearimg(){
// 计算差集
set<string> garbageimg = jedispool.getresource().sdiff(
redisconstant.setmeal_pic_resources,
redisconstant.setmeal_pic_db_resources);
if(garbageimg != null){
for (string imgname : garbageimg) {
// 删除七牛云文件
qiniuutils.deletefilefromqiniu(imgname);
// 删除redis记录
jedispool.getresource().srem(redisconstant.setmeal_pic_resources,imgname);
system.out.println("清理垃圾图片:" + imgname);
}
}
}
}
五、总结
- 图片存储:选用七牛云对象存储,接入简单、cdn 加速、支持 java sdk。
- 套餐管理:多对多关联检查组,支持图片上传预览、分页查询。
- 垃圾清理:基于 redis 集合差集定位垃圾图片,quartz 定时自动清理,节省存储资源。
本文完整实现预约系统套餐管理的图片存储与定时清理,可直接复用至同类项目。
以上就是基于springboot + 七牛云 + quartz实现图片存储与定时清理的详细内容,更多关于springboot七牛云图片存储与定时清理的资料请关注代码网其它相关文章!
发表评论