Spring Boot 2.7.8下载照片Zip包遇HttpMessageNotWritableException报错求助
问题解决:Spring Boot下载照片压缩包报错HttpMessageNotWritableException
问题原因
你代码的核心错误是:已经直接把压缩包内容写入response.getOutputStream(),最后却将关闭后的ZipOutputStream作为ResponseEntity的body返回。Spring MVC会尝试把这个body对象转换为HTTP响应内容,但不存在针对ZipOutputStream的消息转换器,因此抛出HttpMessageNotWritableException。
另外,直接操作HttpServletResponse输出流时,无需返回带body的ResponseEntity——因为响应内容已经通过输出流写完,只需返回空body的响应实体即可。
修正后的代码
@RequestMapping(value = "/api/event/{eventId}/photos/zip", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public ResponseEntity<?> downloadEventPhotosZipAPI(@PathVariable long eventId, HttpServletResponse response) throws Exception { // 设置响应头 response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=event-" + eventId + "-photos.zip"); // 用try-with-resources自动管理流,避免资源泄漏 try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) { List<Attachment> attachmentList = // 从数据库读取活动照片的二进制数据列表... int i = 1; for (Attachment photo : attachmentList) { String filename = "event_photo" + i + ".jpg"; ZipEntry entry = new ZipEntry(filename); zos.putNextEntry(entry); zos.write(photo.getBytes()); zos.closeEntry(); i++; } // 刷新流确保所有内容写出到客户端 zos.flush(); } // 响应内容已通过输出流写完,返回空body的成功响应 return ResponseEntity.ok().build(); }
关键修正点
- 移除无效的body返回:既然已经通过
response输出流写完响应内容,不再需要返回ZipOutputStream作为body,直接返回ResponseEntity.ok().build()即可。 - 用try-with-resources管理流:自动关闭
ZipOutputStream,避免手动关闭可能导致的资源泄漏,同时保证流的正确关闭顺序。 - 简化响应头设置:直接在
response对象上设置内容类型和下载文件名,逻辑更直观,无需重复在ResponseEntity中设置。 - 添加flush操作:确保压缩包的所有内容都被写入输出流,避免客户端接收到不完整的文件。
额外注意事项
- 如果照片数据量较大,建议分批从数据库读取,避免一次性加载所有照片到内存引发OOM。
- 若需要精确设置文件大小,可以提前计算压缩包总大小并设置
Content-Length头;若无需该头,Spring会自动处理分块传输。
内容的提问来源于stack exchange,提问作者jn4
相关产品推荐
相关产品推荐

