Spring Boot接口返回旧Excel数据,求可行解决方案
问题原因
你用getResourceAsStream()读取文件时,这个方法会从类加载器的资源缓存中获取数据——项目启动时Tomcat会把类路径下的资源加载到内存,之后即使服务器上的文件更新了,缓存不会自动刷新,所以返回的永远是启动时的旧数据。
解决方案
根据文件存放位置,分两种常用处理方式:
方式一:直接读取服务器本地文件系统的文件(推荐)
如果Excel文件是存放在服务器的某个本地目录(而非项目的类路径/resource目录),直接通过文件系统路径读取,每次请求都会读取最新的文件内容。
修改后的代码:
import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.apache.commons.io.IOUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @RestController public class DownloadController { // 用@Value注入文件路径,避免硬编码,可在application.properties里配置 @Value("${excel.file.path:/opt/app/data/sample.xlsx}") private String excelFilePath; @GetMapping("/download") public ResponseEntity<byte[]> download() { File excelFile = new File(excelFilePath); // 先检查文件是否存在 if (!excelFile.exists()) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("文件不存在".getBytes()); } // 用try-with-resources自动关闭流,避免资源泄漏 try (FileInputStream is = new FileInputStream(excelFile)) { HttpHeaders headers = new HttpHeaders(); // 处理文件名编码,避免中文乱码 String encodedFileName = new String("sample.xlsx".getBytes("UTF-8"), "ISO-8859-1"); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFileName + "\""); return ResponseEntity.ok() .headers(headers) .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) .body(IOUtils.toByteArray(is)); } catch (IOException e) { e.printStackTrace(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件读取失败".getBytes()); } } }
注意点:
- 把文件路径配置到
application.properties里,比如excel.file.path=/opt/app/data/sample.xlsx,方便后续修改 - 要确保Tomcat进程有该文件的读取权限
- 文件名编码处理:用ISO-8859-1编码,避免不同浏览器下中文文件名乱码
方式二:若文件必须放在类路径下(不推荐生产环境)
如果Excel文件必须放在项目的resources目录下,生产环境可以通过禁用资源缓存或手动刷新类加载器实现,但这种方式性能差且有风险,仅建议开发环境临时用:
@GetMapping("/download") public ResponseEntity<byte[]> download() throws IOException { String fileName = "/sample.xlsx"; // 强制刷新类加载器,重新读取资源 ClassLoader classLoader = this.getClass().getClassLoader(); URL resource = classLoader.getResource(fileName); if (resource == null) { return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); } // 用URL.openStream()而非getResourceAsStream(),避免缓存 try (InputStream is = resource.openStream()) { // 后续header和返回逻辑同方式一 HttpHeaders headers = new HttpHeaders(); String encodedFileName = new String("sample.xlsx".getBytes("UTF-8"), "ISO-8859-1"); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFileName + "\""); return ResponseEntity.ok() .headers(headers) .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) .body(IOUtils.toByteArray(is)); } catch (IOException e) { e.printStackTrace(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } }
注意:
- 这种方式每次请求都会重新加载资源,性能不如直接读取本地文件
- 生产环境中如果项目是打包成jar/war,resources目录里的文件是包内的,无法直接修改,所以这种场景下还是建议用方式一
内容的提问来源于stack exchange,提问作者Ankit Basrur
相关产品推荐
相关产品推荐

