最新产品
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
	 ZZX9599
					ZZX9599