初始化
This commit is contained in:
178
_backup_20260613/FileServiceImpl.java
Normal file
178
_backup_20260613/FileServiceImpl.java
Normal file
@@ -0,0 +1,178 @@
|
||||
package com.mokee.admin.service.impl;
|
||||
|
||||
import cn.hutool.crypto.digest.MD5;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.mokee.admin.mapper.SysFileMapper;
|
||||
import com.mokee.admin.service.ConfigService;
|
||||
import com.mokee.admin.service.FileService;
|
||||
import com.mokee.common.entity.SysFile;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件服务实现 — 文件二进制数据存储于本地磁盘
|
||||
* <p>
|
||||
* 磁盘路径结构:{basePath}/{yyyy-MM}/{storageFileName}
|
||||
* 例:/data/mokee/gw/file/2026-06/a1b2c3d4e5f6.png
|
||||
* <p>
|
||||
* 向下兼容:存量 BLOB 数据(file_data 不为空)仍可正常读取。
|
||||
*
|
||||
* @author mokee
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FileServiceImpl implements FileService {
|
||||
|
||||
private final SysFileMapper sysFileMapper;
|
||||
private final ConfigService configService;
|
||||
|
||||
private static final DateTimeFormatter YYYY_MM = DateTimeFormatter.ofPattern("yyyy-MM");
|
||||
private static final String DEFAULT_BASE_PATH = "/data/mokee/gw/file";
|
||||
|
||||
/** 获取配置的存储根目录 */
|
||||
private String getBasePath() {
|
||||
return configService.getValue("file.storage.disk.path", DEFAULT_BASE_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从原始文件名提取扩展名
|
||||
*/
|
||||
private String ext(String fileName) {
|
||||
if (fileName == null) return "";
|
||||
int i = fileName.lastIndexOf('.');
|
||||
return i > 0 ? fileName.substring(i) : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysFile upload(MultipartFile file, String fileType, String systemId,
|
||||
String userId, String uploadUserName) throws IOException {
|
||||
byte[] data = file.getBytes();
|
||||
|
||||
// 计算 MD5
|
||||
String md5 = MD5.create().digestHex(data);
|
||||
|
||||
// 构建实体(先生成 ID 用于存储文件名)
|
||||
String fileId = cn.hutool.core.lang.UUID.randomUUID().toString().replace("-", "");
|
||||
String storageFileName = fileId + ext(file.getOriginalFilename());
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
SysFile sf = SysFile.builder()
|
||||
.id(fileId)
|
||||
.systemId(systemId)
|
||||
.fileName(file.getOriginalFilename())
|
||||
.storageFileName(storageFileName)
|
||||
.fileType(fileType != null ? fileType : "other")
|
||||
.fileSize(file.getSize())
|
||||
.mimeType(file.getContentType())
|
||||
.md5(md5)
|
||||
.uploadUserId(userId)
|
||||
.uploadUserName(uploadUserName)
|
||||
.fileData(null) // 新文件不写入 BLOB
|
||||
.createdAt(now)
|
||||
.build();
|
||||
|
||||
// 写入磁盘
|
||||
String basePath = getBasePath();
|
||||
Path dir = Paths.get(basePath, now.format(YYYY_MM));
|
||||
try {
|
||||
Files.createDirectories(dir);
|
||||
} catch (IOException e) {
|
||||
log.error("创建存储目录失败: {} — 请检查磁盘权限和路径配置", dir);
|
||||
throw new IOException("存储目录创建失败: " + dir + " (请检查磁盘权限和路径配置)", e);
|
||||
}
|
||||
Path filePath = dir.resolve(storageFileName);
|
||||
try {
|
||||
Files.write(filePath, data);
|
||||
} catch (IOException e) {
|
||||
log.error("写入文件失败: {} — 可能磁盘空间不足或权限不足", filePath);
|
||||
throw new IOException("文件写入失败: " + filePath + " (可能磁盘空间不足或权限不足)", e);
|
||||
}
|
||||
|
||||
log.info("文件已写入磁盘: {} ({} bytes, md5={})", filePath, file.getSize(), md5);
|
||||
|
||||
// 存元信息到 DB
|
||||
sysFileMapper.insert(sf);
|
||||
|
||||
return sf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<SysFile> page(Integer page, Integer size, String fileType,
|
||||
String fileName, String uploadUserName, String systemId) {
|
||||
LambdaQueryWrapper<SysFile> w = new LambdaQueryWrapper<>();
|
||||
if (fileType != null && !fileType.isEmpty()) w.eq(SysFile::getFileType, fileType);
|
||||
if (fileName != null && !fileName.isEmpty()) w.like(SysFile::getFileName, fileName);
|
||||
if (uploadUserName != null && !uploadUserName.isEmpty()) w.like(SysFile::getUploadUserName, uploadUserName);
|
||||
if (systemId != null) w.eq(SysFile::getSystemId, systemId);
|
||||
w.orderByDesc(SysFile::getCreatedAt);
|
||||
w.select(SysFile.class, f -> !"file_data".equals(f.getColumn()));
|
||||
return sysFileMapper.selectPage(new Page<>(page, size), w);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysFile> list(String fileType, String systemId) {
|
||||
LambdaQueryWrapper<SysFile> w = new LambdaQueryWrapper<>();
|
||||
if (fileType != null && !fileType.isEmpty()) w.eq(SysFile::getFileType, fileType);
|
||||
if (systemId != null) w.eq(SysFile::getSystemId, systemId);
|
||||
w.orderByDesc(SysFile::getCreatedAt);
|
||||
w.select(SysFile.class, f -> !"file_data".equals(f.getColumn()));
|
||||
return sysFileMapper.selectList(w);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysFile getById(String id) {
|
||||
return sysFileMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysFile getMetaById(String id) {
|
||||
LambdaQueryWrapper<SysFile> w = new LambdaQueryWrapper<>();
|
||||
w.eq(SysFile::getId, id);
|
||||
w.select(SysFile.class, f -> !"file_data".equals(f.getColumn()));
|
||||
return sysFileMapper.selectOne(w);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getDiskPath(SysFile sf) {
|
||||
String basePath = getBasePath();
|
||||
// 存量兼容:旧文件 storageFileName 为空时,用 id + 扩展名兜底
|
||||
String storageName = sf.getStorageFileName();
|
||||
if (storageName == null || storageName.isEmpty()) {
|
||||
storageName = sf.getId() + ext(sf.getFileName());
|
||||
}
|
||||
return Paths.get(basePath, sf.getCreatedAt().format(YYYY_MM), storageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) throws IOException {
|
||||
SysFile sf = sysFileMapper.selectById(id);
|
||||
if (sf == null) return;
|
||||
|
||||
// 删除磁盘文件(失败不阻断 DB 删除)
|
||||
try {
|
||||
Path path = getDiskPath(sf);
|
||||
if (Files.exists(path)) {
|
||||
Files.delete(path);
|
||||
log.info("磁盘文件已删除: {}", path);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("删除磁盘文件失败(继续删DB记录) - id: {}, error: {}", id, e.getMessage());
|
||||
}
|
||||
|
||||
// 删除 DB 记录
|
||||
sysFileMapper.deleteById(id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user