feat(image): 添加图片清理功能- 新增 FileStorageService 用于文件上传、下载和删除操作- 实现 ImageCleanupService 清理冗余图片

- 添加 ImageCleanupController 提供手动清理图片的 API
- 创建 ImageCleanupScheduler定时清理冗余图片
- 更新相关 Mapper 接口,增加必要的查询和删除方法
This commit is contained in:
2025-08-06 17:29:11 +08:00
parent 8cdba1c0e6
commit b28446c4b6
8 changed files with 211 additions and 2 deletions

View File

@@ -0,0 +1,32 @@
package com.test.bijihoudaun.controller;
import com.test.bijihoudaun.service.ImageCleanupService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 图片清理控制器
* 提供手动触发清理冗余图片的API端点
*/
@RestController
@RequestMapping("/api/admin")
public class ImageCleanupController {
@Autowired
private ImageCleanupService imageCleanupService;
/**
* 手动触发清理冗余图片
* @return 清理结果
*/
@PostMapping("/cleanup-images")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> cleanupImages() {
int deletedCount = imageCleanupService.cleanupRedundantImages();
return ResponseEntity.ok().body("成功清理 " + deletedCount + " 个冗余图片");
}
}

View File

@@ -3,6 +3,8 @@ package com.test.bijihoudaun.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.test.bijihoudaun.entity.Image;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface ImageMapper extends BaseMapper<Image> {
@@ -11,4 +13,11 @@ public interface ImageMapper extends BaseMapper<Image> {
// 自定义方法根据Markdown文件ID获取关联图片
// List<Image> findByMarkdownId(Integer markdownId);
/**
* 获取所有图片记录
* @return 所有图片列表
*/
@Select("SELECT * FROM image")
List<Image> findAll();
}

View File

@@ -3,8 +3,17 @@ package com.test.bijihoudaun.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.test.bijihoudaun.entity.ImageName;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Delete;
@Mapper
public interface ImageNameMapper extends BaseMapper<ImageName> {
// 可以在这里添加自定义方法
/**
* 根据文件名删除记录
* @param fileName 文件名
* @return 删除的记录数
*/
@Delete("DELETE FROM image_name WHERE file_name = #{fileName}")
int deleteByFileName(String fileName);
}

View File

@@ -43,4 +43,11 @@ public interface MarkdownFileMapper extends BaseMapper<MarkdownFile> {
void restoreById(@Param("id") Long id);
@Delete("DELETE FROM markdown_file WHERE is_deleted = 1")
void physicalDeleteByIsDeleted();
/**
* 获取所有笔记ID
* @return 所有笔记ID列表
*/
@Select("SELECT id FROM markdown_file WHERE is_deleted = 0")
List<Integer> findAllIds();
}

View File

@@ -0,0 +1,27 @@
package com.test.bijihoudaun.scheduler;
import com.test.bijihoudaun.service.ImageCleanupService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 图片清理定时任务
* 定期自动清理冗余图片
*/
@Component
public class ImageCleanupScheduler {
@Autowired
private ImageCleanupService imageCleanupService;
/**
* 定时清理任务
* 每天凌晨3点执行一次清理任务
*/
@Scheduled(cron = "0 0 3 * * ?")
public void scheduledCleanup() {
int deletedCount = imageCleanupService.cleanupRedundantImages();
System.out.println("定时清理任务完成,清理了 " + deletedCount + " 个冗余图片");
}
}

View File

@@ -0,0 +1,44 @@
package com.test.bijihoudaun.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* 文件存储服务
* 用于处理文件上传、下载和删除等操作
*/
@Service
public class FileStorageService {
@Value("${file.upload-dir}")
private String uploadDir;
/**
* 加载文件作为File对象
* @param filename 文件名
* @return 文件对象
*/
public java.io.File loadAsFile(String filename) {
return new java.io.File(uploadDir + java.io.File.separator + filename);
}
/**
* 删除文件
* @param filename 文件名
* @return 是否删除成功
*/
public boolean deleteFile(String filename) {
try {
Path filePath = Paths.get(uploadDir, filename);
return Files.deleteIfExists(filePath);
} catch (IOException e) {
System.err.println("Failed to delete file: " + filename);
return false;
}
}
}

View File

@@ -0,0 +1,81 @@
package com.test.bijihoudaun.service;
import com.test.bijihoudaun.util.MarkdownImageExtractor;
import com.test.bijihoudaun.mapper.ImageMapper;
import com.test.bijihoudaun.mapper.MarkdownFileMapper;
import com.test.bijihoudaun.mapper.ImageNameMapper;
import com.test.bijihoudaun.entity.Image;
import com.test.bijihoudaun.entity.MarkdownFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
/**
* 图片清理服务类
* 用于清理不再被笔记引用的冗余图片
*/
@Service
public class ImageCleanupService {
@Autowired
private ImageMapper imageMapper;
@Autowired
private MarkdownFileMapper markdownFileMapper;
@Autowired
private ImageNameMapper imageNameMapper;
@Autowired
private FileStorageService fileStorageService;
/**
* 清理冗余图片
* @return 清理的图片数量
*/
@Transactional
public int cleanupRedundantImages() {
// 获取所有笔记ID
List<Integer> allMarkdownIds = markdownFileMapper.findAllIds();
// 用于存储所有被引用的图片文件名
Set<String> referencedImageNames = new HashSet<>();
// 遍历所有笔记,收集被引用的图片
for (Integer markdownId : allMarkdownIds) {
MarkdownFile markdownFile = markdownFileMapper.selectById(markdownId);
String content = (markdownFile != null) ? markdownFile.getContent() : "";
List<String> imageNames = MarkdownImageExtractor.extractImageFilenames(content);
referencedImageNames.addAll(imageNames);
}
// 获取所有图片记录
List<Image> allImages = imageMapper.findAll();
// 记录被删除的图片数量
int deletedCount = 0;
// 遍历所有图片,删除未被引用的
for (Image image : allImages) {
if (!referencedImageNames.contains(image.getStoredName())) {
// 删除物理文件
fileStorageService.deleteFile(image.getStoredName());
// 删除数据库记录
imageMapper.deleteById(image.getId());
// 如果存在image_name表中的记录也一并删除
imageNameMapper.deleteByFileName(image.getStoredName());
deletedCount++;
}
}
return deletedCount;
}
}

View File

@@ -1,8 +1,8 @@
spring:
datasource:
driver-class-name: org.sqlite.JDBC
url: jdbc:sqlite:C:\it\houtaigunli\biji\mydatabase.db
# url: jdbc:sqlite:C:\KAIFA\2\mydatabase.db
# url: jdbc:sqlite:C:\it\houtaigunli\biji\mydatabase.db
url: jdbc:sqlite:C:\KAIFA\2\mydatabase.db
jpa:
hibernate:
ddl-auto: none