基于SpringBoot + 七牛云 + Quartz實現(xiàn)圖片存儲與定時清理
一、圖片存儲方案
1.1 常見圖片存儲方案

實際項目中會拆分不同功能服務(wù)器,提升系統(tǒng)運行效率,圖片存儲常用三種方案:
- Nginx 搭建圖片服務(wù)器
- 分布式文件存儲系統(tǒng)(FastDFS、HDFS)
- 云存儲(阿里云 OSS、七牛云)
本文選用七牛云對象存儲,接入簡單、CDN 加速快、適合中小項目快速落地。
1.2 七牛云使用流程
1.2.1 注冊與實名認證
- 訪問七牛云官網(wǎng):https://www.qiniu.com/
- 注冊賬號并完成個人實名認證(創(chuàng)建存儲空間必須認證)
1.2.2 新建存儲空間
控制臺進入對象存儲 KODO

- 創(chuàng)建存儲空間(Bucket):
- 名稱:3~63 位小寫字母 / 數(shù)字 / 短橫線
- 存儲區(qū)域:華東 / 華北 / 華南等
- 訪問控制:公開 / 私有(圖片通常設(shè)為公開)

- 創(chuàng)建成功后可在文件管理查看上傳資源。
1.2.3 獲取 AK/SK 密鑰
- 進入個人中心 → 密鑰管理
- 復(fù)制 AccessKey 和 SecretKey(Java SDK 鑒權(quán)使用)

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()); //你自己存儲空間的存儲區(qū)域
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();
}
}
// 字節(jié)數(shù)組上傳
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());
}
}
}
?二、新增套餐功能(圖片上傳 + 多對多關(guān)聯(lián))
2.1 需求說明
套餐是檢查組的集合,套餐與檢查組為多對多關(guān)系,需中間表t_setmeal_checkgroup關(guān)聯(lián)。新增套餐需錄入:
- 基本信息(編碼、名稱、價格、圖片等)
- 關(guān)聯(lián)檢查組
2.2 前端實現(xiàn)(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 圖片上傳與預(yù)覽
使用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 后端實現(xiàn)
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 新增套餐(事務(wù) + 多對多關(guān)聯(lián))
@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();
// 設(shè)置套餐與檢查組關(guān)聯(lián)
this.setSetmealAndCheckgroup(setmealId,checkgroupIds);
}
// 維護多對多關(guān)系
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(已保存到數(shù)據(jù)庫的圖片)
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);
}
}
三、定時任務(wù)組件 Quartz(重點)
3.1 Quartz 核心概念
- Job:要執(zhí)行的任務(wù)(清理圖片)
- Trigger:觸發(fā)器(定義執(zhí)行時間)
- Scheduler:調(diào)度器(綁定任務(wù)與觸發(fā)器)

3.2 Cron 表達式
格式:秒 分 時 日 月 周 年(年可省略)常用示例:
0/1 * * * * ?:每秒執(zhí)行0 0 2 * * ?:每天凌晨 2 點執(zhí)行0 0 0/1 * * ?:每小時執(zhí)行


示例:

前面介紹了cron表達式,但是自己編寫表達式還是有一些困難的,我們可以借助一些 cron表達式在線生成器來根據(jù)我們的需求生成表達式即可。
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 垃圾圖片產(chǎn)生原因
用戶上傳圖片后未提交套餐,圖片存于七牛云但無數(shù)據(jù)庫記錄,成為垃圾文件。
4.2 清理思路
- Redis 集合 A:
setmealPicResources(所有上傳圖片) - Redis 集合 B:
setmealPicDbResources(已保存數(shù)據(jù)庫圖片) - 求差集:A - B = 垃圾圖片
- 定時刪除七牛云 + Redis 中的垃圾文件
4.3 清理任務(wù)實現(xiàn)
@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);
}
}
}
}
?五、總結(jié)
- 圖片存儲:選用七牛云對象存儲,接入簡單、CDN 加速、支持 Java SDK。
- 套餐管理:多對多關(guān)聯(lián)檢查組,支持圖片上傳預(yù)覽、分頁查詢。
- 垃圾清理:基于 Redis 集合差集定位垃圾圖片,Quartz 定時自動清理,節(jié)省存儲資源。
本文完整實現(xiàn)預(yù)約系統(tǒng)套餐管理的圖片存儲與定時清理,可直接復(fù)用至同類項目。
以上就是基于SpringBoot + 七牛云 + Quartz實現(xiàn)圖片存儲與定時清理的詳細內(nèi)容,更多關(guān)于SpringBoot七牛云圖片存儲與定時清理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解設(shè)計模式中的proxy代理模式及在Java程序中的實現(xiàn)
代理模式主要分為靜態(tài)代理和動態(tài)代理,使客戶端方面的使用者通過設(shè)置的代理來操作對象,下面來詳解設(shè)計模式中的proxy代理模式及在Java程序中的實現(xiàn)2016-05-05
Java中字符串轉(zhuǎn)時間與時間轉(zhuǎn)字符串的操作詳解
Java 的 java.time 包提供了強大的日期和時間處理功能,通過 DateTimeFormatter 可以輕松地在日期時間對象和字符串之間進行轉(zhuǎn)換,下面小編給大家詳細介紹一下Java中字符串轉(zhuǎn)時間與時間轉(zhuǎn)字符串的操作,需要的朋友可以參考下2025-04-04
IntelliJ IDEA新版本中Maven子模塊不顯示的解決方案
在使用IntelliJ IDEA 2024版本開發(fā)Maven多模塊項目時,我發(fā)現(xiàn)一個令人困惑的現(xiàn)象,父模塊的子模塊未在右側(cè) Maven 工具窗口中顯示,僅顯示父模塊名稱,而此前2021版本中,父模塊和子模塊均能正常顯示,所以本文給大家介紹了IntelliJ IDEA新版本中Maven子模塊不顯示的解決方案2025-05-05
Spring MVC 更靈活的控制 json 返回問題(自定義過濾字段)
本篇文章主要介紹了Spring MVC 更靈活的控制 json 返回問題(自定義過濾字段),具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-02-02
Mapper報錯@MapKey is required問題及解決方式
這段描述主要討論了在使用MyBatis或類似框架時遇到@MapKeyisrequired錯誤的常見原因及解決方法,包括MapKey注解的正確使用以及相關(guān)配置檢查,特別強調(diào)了正確配置isMapMapKey和ResultMap注解對于避免數(shù)據(jù)不一致和運行時異常的重要性2026-06-06

