package com.controller; import java.io.File; import java.io.IOException; import java.util.Date; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.ResourceUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import com.annotation.IgnoreAuth; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.entity.ConfigEntity; import com.entity.EIException; import com.service.ConfigService; import com.utils.R; /** * 文件管理系统控制器 * 核心功能:处理文件上传/下载请求,管理系统静态资源 * 安全策略:@IgnoreAuth 使下载接口可公开访问 */ @RestController @RequestMapping("file") // 基础访问路径 /file public class FileController { @Autowired private ConfigService configService; // 配置服务(用于存储特殊文件路径) // ======================== 文件上传接口 ======================== /** * 文件上传服务 * 路径:POST /file/upload * 参数: * @RequestParam("file") MultipartFile file - 上传的文件对象 * String type - 文件类型标识(特殊处理标记) * 流程: * 1. 空文件校验 → 抛出自定义异常 EIException * 2. 提取文件后缀名(fileExt) * 3. 获取项目static资源目录路径 * 4. 在static下创建upload目录(不存在时自动创建) * 5. 生成时间戳文件名(避免重名覆盖) * 6. 保存文件到服务器 * 7. 特殊类型文件(type="1")更新系统配置 * 返回值:R.ok().put("file", fileName) 返回新文件名 */ @RequestMapping("/upload") @IgnoreAuth // 允许匿名上传(根据需求谨慎设置) public R upload(@RequestParam("file") MultipartFile file, String type) throws Exception { // 1. 空文件校验 if (file.isEmpty()) { throw new EIException("上传文件不能为空"); } // 2. 提取文件后缀(如:png) String fileExt = file.getOriginalFilename() .substring(file.getOriginalFilename().lastIndexOf(".") + 1); // 3. 获取static资源目录 File path = new File(ResourceUtils.getURL("classpath:static").getPath()); if (!path.exists()) path = new File(""); // 4. 创建upload目录 File uploadDir = new File(path, "/upload/"); if (!uploadDir.exists()) uploadDir.mkdirs(); // 5. 构建新文件名(时间戳防重名) String fileName = new Date().getTime() + "." + fileExt; File dest = new File(uploadDir, fileName); // 6. 保存文件 file.transferTo(dest); /* * 热部署文件同步(开发环境专用) * 解除注释并将路径改为本地项目static绝对路径 * 示例:FileUtils.copyFile(dest, new File("D:/project/src/main/resources/static/upload/"+fileName)); */ // 7. 特殊文件类型处理(如人脸文件) if ("1".equals(type)) { // 查询或创建faceFile配置项 ConfigEntity faceConfig = configService.selectOne( new EntityWrapper().eq("name", "faceFile") ); if (faceConfig == null) { faceConfig = new ConfigEntity(); faceConfig.setName("faceFile"); faceConfig.setValue(fileName); configService.insert(faceConfig); } else { faceConfig.setValue(fileName); configService.updateById(faceConfig); } } return R.ok().put("file", fileName); } // ======================== 文件下载接口 ======================== /** * 文件下载服务 * 路径:GET /file/download?fileName={文件名} * 流程: * 1. 定位static/upload目录 * 2. 检查文件是否存在 * 3. 设置HTTP响应头: * - ContentType: APPLICATION_OCTET_STREAM(二进制流) * - Content-Disposition: attachment(触发浏览器下载) * 4. 将文件转为字节流返回 * 安全说明:未做权限校验(@IgnoreAuth),生产环境需添加安全控制 */ @IgnoreAuth // 允许匿名下载 @RequestMapping("/download") public ResponseEntity download(@RequestParam String fileName) { try { // 1. 获取static目录路径 File staticDir = new File(ResourceUtils.getURL("classpath:static").getPath()); if (!staticDir.exists()) staticDir = new File(""); // 2. 定位upload目录 File uploadDir = new File(staticDir, "/upload/"); if (!uploadDir.exists()) uploadDir.mkdirs(); // 3. 构建目标文件 File targetFile = new File(uploadDir, fileName); // 4. 文件存在性检查 if (targetFile.exists()) { // 5. 设置响应头(触发下载) HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.setContentDispositionFormData("attachment", fileName); // 6. 返回文件字节流 return new ResponseEntity<>( FileUtils.readFileToByteArray(targetFile), headers, HttpStatus.OK ); } } catch (IOException e) { e.printStackTrace(); } // 7. 文件不存在或IO错误 return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } }