Spring 4.3.14使用InputStreamResource下载文件损坏问题求助
Hey there, let's break down why your file downloads are getting corrupted when using InputStreamResource in Spring 4.3.14, and fix it up.
Root Cause
The main issue here boils down to how InputStreamResource interacts with Spring's ResourceHttpMessageConverter:
- Unlike
ByteArrayResourcewhich holds your entire file content in memory,InputStreamResourcegenerates a fresh input stream every timegetInputStream()is called. - In Spring 4.3.x,
ResourceHttpMessageConverterfirst callsresource.contentLength()to calculate the file size. ForInputStreamResource, this method reads the entire stream to get the length—by the time the converter tries to write the stream to the response, the stream pointer is already at the end, resulting in incomplete or empty (corrupted) files.
Solutions
1. Switch to ByteArrayResource (Best for Small-to-Medium Files)
Replace InputStreamResource with ByteArrayResource, which safely supports multiple stream reads since it stores content in memory. Here's a complete controller example:
@RequestMapping(value = "/download", method = RequestMethod.GET) public ResponseEntity<Resource> downloadFile() throws IOException { // Replace with your actual file source (local file, DB blob, etc.) File targetFile = new File("/path/to/your/file.pdf"); byte[] fileContent = Files.readAllBytes(targetFile.toPath()); ByteArrayResource resource = new ByteArrayResource(fileContent); return ResponseEntity.ok() // Trigger browser save dialog .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + targetFile.getName() + "\"") // Explicitly set content length to avoid browser guessing .contentLength(fileContent.length) // Adjust media type to match your file type .contentType(MediaType.APPLICATION_PDF) .body(resource); }
2. Use StreamingResponseBody (Best for Large Files)
If your files are too big to load into memory, use StreamingResponseBody. This approach streams content directly to the response without holding the entire file in memory, and avoids the repeated stream reads that break InputStreamResource:
@RequestMapping(value = "/download", method = RequestMethod.GET) public ResponseEntity<StreamingResponseBody> downloadLargeFile() throws IOException { File largeFile = new File("/path/to/large/file.zip"); InputStream fileInputStream = new FileInputStream(largeFile); StreamingResponseBody responseBody = outputStream -> { byte[] buffer = new byte[4096]; // 4KB buffer for efficient streaming int bytesRead; while ((bytesRead = fileInputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } // Clean up resources fileInputStream.close(); }; return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + largeFile.getName() + "\"") .contentLength(largeFile.length()) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(responseBody); }
3. Bonus: Simplify Your WebMvcConfig
You don't need to explicitly add ResourceHttpMessageConverter to the converters list—Spring 4.3.x registers it by default. Removing that custom configuration can avoid accidental conflicts with default converter behavior.
内容的提问来源于stack exchange,提问作者Angelo Immediata

