最新产品
This commit is contained in:
@ -0,0 +1,241 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
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.yj.earth.business.domain.FileInfo;
|
||||
import com.yj.earth.business.service.FileInfoService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.vo.FileInfoVo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Tag(name = "文件数据管理")
|
||||
@RestController
|
||||
@RequestMapping("/fileInfo")
|
||||
public class FileInfoController {
|
||||
|
||||
@Resource
|
||||
private FileInfoService fileInfoService;
|
||||
|
||||
@Value("${file.upload.path}")
|
||||
private String uploadPath;
|
||||
|
||||
// 获取项目根目录
|
||||
private String getProjectRootPath() {
|
||||
return System.getProperty("user.dir");
|
||||
}
|
||||
|
||||
// 获取完整的上传目录路径
|
||||
private String getFullUploadPath() {
|
||||
// 拼接项目根目录和配置的上传路径
|
||||
return getProjectRootPath() + File.separator + uploadPath;
|
||||
}
|
||||
|
||||
@Operation(summary = "文件上传")
|
||||
@PostMapping("/upload")
|
||||
public ApiResponse uploadFiles(@Parameter(description = "上传的文件数组", required = true) @RequestParam("files") MultipartFile[] files) throws IOException {
|
||||
|
||||
// 校验文件数组是否为空
|
||||
if (files == null || files.length == 0) {
|
||||
return ApiResponse.failure("上传文件不能为空");
|
||||
}
|
||||
|
||||
// 获取完整的上传目录路径
|
||||
String fullUploadPath = getFullUploadPath();
|
||||
|
||||
List<FileInfoVo> fileInfoVoList = new ArrayList<>();
|
||||
|
||||
// 遍历处理每个文件
|
||||
for (MultipartFile file : files) {
|
||||
// 跳过空文件
|
||||
if (file.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取原始文件名和后缀
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String fileSuffix = FileUtil.extName(originalFilename);
|
||||
String contentType = file.getContentType();
|
||||
|
||||
// 生成唯一文件名、避免重复
|
||||
String uniqueFileName = IdUtil.simpleUUID() + "." + fileSuffix;
|
||||
|
||||
// 创建文件存储目录(如果不存在则自动创建)
|
||||
File uploadDir = new File(fullUploadPath);
|
||||
FileUtil.mkdir(uploadDir);
|
||||
|
||||
// 构建完整文件路径
|
||||
String filePath = fullUploadPath + File.separator + uniqueFileName;
|
||||
File destFile = new File(filePath);
|
||||
|
||||
// 使用Hutool工具类保存文件
|
||||
FileUtil.writeBytes(file.getBytes(), destFile);
|
||||
|
||||
// 计算文件MD5(用于校验文件完整性)
|
||||
String fileMd5 = DigestUtil.md5Hex(destFile);
|
||||
|
||||
// 查询有没有文件名一样并且 MD5 也一样的数据
|
||||
LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FileInfo::getFileName, originalFilename).eq(FileInfo::getFileMd5, fileMd5);
|
||||
if (fileInfoService.count(queryWrapper) > 0) { // 修复了此处的bug、添加了queryWrapper参数
|
||||
return ApiResponse.failure("已存在文件名相同且内容完全一致的文件");
|
||||
}
|
||||
|
||||
// 保存文件信息到数据库
|
||||
FileInfo fileInfo = new FileInfo();
|
||||
fileInfo.setFileName(originalFilename);
|
||||
fileInfo.setFileSuffix(fileSuffix);
|
||||
fileInfo.setContentType(contentType);
|
||||
fileInfo.setFileSize(file.getSize());
|
||||
fileInfo.setFilePath(uniqueFileName); // 只保存相对文件名、不保存完整路径
|
||||
fileInfo.setFileMd5(fileMd5);
|
||||
|
||||
// 保存文件信息并获取ID
|
||||
fileInfoService.save(fileInfo);
|
||||
|
||||
// 构建并设置预览URL和下载URL
|
||||
String previewUrl = "/fileInfo/preview/" + fileInfo.getId();
|
||||
String downloadUrl = "/fileInfo/download/" + fileInfo.getId();
|
||||
|
||||
FileInfoVo fileInfoVo = new FileInfoVo();
|
||||
BeanUtils.copyProperties(fileInfo, fileInfoVo);
|
||||
fileInfoVo.setPreviewUrl(previewUrl);
|
||||
fileInfoVo.setDownloadUrl(downloadUrl);
|
||||
|
||||
fileInfoVoList.add(fileInfoVo);
|
||||
}
|
||||
|
||||
if (fileInfoVoList.isEmpty()) {
|
||||
return ApiResponse.failure("未成功上传任何文件");
|
||||
}
|
||||
|
||||
return ApiResponse.success(fileInfoVoList);
|
||||
}
|
||||
|
||||
@Operation(summary = "文件下载")
|
||||
@GetMapping("/download/{id}")
|
||||
public void downloadFile(@Parameter(description = "文件ID", required = true) @PathVariable String id, HttpServletResponse response) throws IOException {
|
||||
// 根据ID查询文件信息
|
||||
FileInfo fileInfo = fileInfoService.getById(id);
|
||||
if (fileInfo == null) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建完整文件路径
|
||||
String fullPath = getFullUploadPath() + File.separator + fileInfo.getFilePath();
|
||||
File file = new File(fullPath);
|
||||
|
||||
// 校验文件是否存在
|
||||
if (!file.exists()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
response.setContentType(fileInfo.getContentType());
|
||||
response.setContentLengthLong(fileInfo.getFileSize());
|
||||
String encodedFileName = URLEncoder.encode(fileInfo.getFileName(), StandardCharsets.UTF_8.name());
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
|
||||
|
||||
// 使用 Hutool 工具类复制文件流到响应输出流
|
||||
try (OutputStream os = response.getOutputStream()) {
|
||||
FileUtil.writeToStream(file, os);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "文件预览")
|
||||
@GetMapping("/preview/{id}")
|
||||
public void previewFile(
|
||||
@Parameter(description = "文件ID", required = true)
|
||||
@PathVariable String id,
|
||||
HttpServletResponse response) throws IOException {
|
||||
|
||||
// 根据ID查询文件信息
|
||||
FileInfo fileInfo = fileInfoService.getById(id);
|
||||
if (fileInfo == null) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建完整文件路径
|
||||
String fullPath = getFullUploadPath() + File.separator + fileInfo.getFilePath();
|
||||
File file = new File(fullPath);
|
||||
|
||||
// 校验文件是否存在
|
||||
if (!file.exists()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置响应头(不设置 attachment、让浏览器直接显示)
|
||||
response.setContentType(fileInfo.getContentType());
|
||||
response.setContentLengthLong(fileInfo.getFileSize());
|
||||
|
||||
// 使用 Hutool 工具类复制文件流到响应输出流
|
||||
try (OutputStream os = response.getOutputStream()) {
|
||||
FileUtil.writeToStream(file, os);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "文件列表")
|
||||
@GetMapping("/list")
|
||||
public ApiResponse getFileList(
|
||||
@Parameter(description = "页码", required = true) Integer pageNum,
|
||||
@Parameter(description = "每页条数", required = true) Integer pageSize,
|
||||
@Parameter(description = "文件名称") String fileName) {
|
||||
|
||||
// 创建分页对象
|
||||
Page<FileInfo> page = new Page<>(pageNum, pageSize);
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
|
||||
if (fileName != null && !fileName.isEmpty()) {
|
||||
queryWrapper.like(FileInfo::getFileName, fileName);
|
||||
}
|
||||
// 按创建时间倒序排列、最新上传的文件在前
|
||||
queryWrapper.orderByDesc(FileInfo::getCreatedAt);
|
||||
|
||||
// 执行分页查询
|
||||
IPage<FileInfo> fileInfoPage = fileInfoService.page(page, queryWrapper);
|
||||
|
||||
// 转换为VO对象并设置URL
|
||||
List<FileInfoVo> records = fileInfoPage.getRecords().stream().map(fileInfo -> {
|
||||
FileInfoVo vo = new FileInfoVo();
|
||||
BeanUtils.copyProperties(fileInfo, vo);
|
||||
vo.setPreviewUrl("/fileInfo/preview/" + fileInfo.getId());
|
||||
vo.setDownloadUrl("/fileInfo/download/" + fileInfo.getId());
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
// 构建分页结果
|
||||
Page<FileInfoVo> resultPage = new Page<>();
|
||||
resultPage.setRecords(records);
|
||||
resultPage.setTotal(fileInfoPage.getTotal());
|
||||
resultPage.setSize(fileInfoPage.getSize());
|
||||
resultPage.setCurrent(fileInfoPage.getCurrent());
|
||||
resultPage.setPages(fileInfoPage.getPages());
|
||||
return ApiResponse.success(resultPage);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user