Mac下载后生成的Zip文件始终损坏问题排查求助
问题:Mac无法解压生成的Zip文件,提示格式不支持
将一组对象打包为Zip文件,已实现如下Zip转换类和Spring控制器,但在Mac上下载生成的Zip文件后,始终无法解压,系统提示“Unable to expand filename.zip. It is in an unsupported format.”。尝试调整流的关闭方式、修改ContentType等操作均无效,恳请帮忙排查问题所在。
Zip转换类代码
public class ZipFile { public byte[] createZipByteArray(String fileName, byte[] content) throws IOException { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try (byteArrayOutputStream; ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) { zipOutputStream.setLevel(Deflater.NO_COMPRESSION); ZipEntry zipEntry = new ZipEntry(fileName); zipOutputStream.putNextEntry(zipEntry); zipOutputStream.write(content); zipOutputStream.closeEntry(); zipOutputStream.flush(); } return byteArrayOutputStream.toByteArray(); } }
Spring控制器代码
@GetMapping(value = "objectWithDetails/zip", produces = "application/zip") @RolesAllowed("QUERY_DATA") public ResponseEntity<byte[]> getObjectWithDetails(String limit, HttpServletResponse response) throws IOException { response.setContentType("application/zip"); List<ObjectSpecification> objectRepresentation = recipeService.getObjectRepresentation(limit); byte[] objectsBytes = objectMapper.writeValueAsBytes(objectRepresentation); ZipFile zipFile = new ZipFile(); return ResponseEntity .ok() .contentType(MediaType.valueOf("application/zip")) .header("Content-Encoding", "UTF-8") .header("Content-Disposition", String.format("attachment; filename=\"details_%s.zip\"", dateTimeProvider.nowDate())) .body(zipFile.createZipByteArray(String.format("details_%s.zip", dateTimeProvider.nowDate()), objectsBytes)); }
问题排查与修复方案
核心问题1:错误设置Content-Encoding头
控制器中添加了Content-Encoding: UTF-8,这个头用于标识文本内容的编码格式,但Zip是二进制文件,添加该头会让Mac的解压工具错误地尝试用UTF-8解码二进制数据,直接导致格式识别失败。
核心问题2(非致命但需修正):Zip内部条目命名不合理
原代码中把Zip包内的文件命名为.zip,但实际内容是JSON字节数组,这种命名会造成混淆,也可能让系统误判文件类型。
修改后的控制器代码
@GetMapping(value = "objectWithDetails/zip", produces = "application/zip") @RolesAllowed("QUERY_DATA") public ResponseEntity<byte[]> getObjectWithDetails(String limit) throws IOException { List<ObjectSpecification> objectRepresentation = recipeService.getObjectRepresentation(limit); byte[] objectsBytes = objectMapper.writeValueAsBytes(objectRepresentation); ZipFile zipFile = new ZipFile(); String dateStr = dateTimeProvider.nowDate(); // 确保日期字符串不包含Mac文件名禁止的字符(如冒号) dateStr = dateStr.replaceAll("[:/]", "-"); return ResponseEntity .ok() .contentType(MediaType.APPLICATION_OCTET_STREAM) // 用二进制流类型更稳妥 .header("Content-Disposition", String.format("attachment; filename=\"details_%s.zip\"", dateStr)) .body(zipFile.createZipByteArray(String.format("details_%s.json", dateStr), objectsBytes)); }
额外注意事项
- 移除了
response.setContentType("application/zip"),因为ResponseEntity已经设置了ContentType,重复设置可能引发冲突。 - 对日期字符串做了特殊字符替换,Mac系统不允许文件名包含冒号、斜杠等字符,避免因文件名非法导致文件损坏。
- Zip转换类的代码无需修改,try-with-resources的写法在Java 9及以上版本是合法的,若使用Java 8及以下,需调整为:
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) { // 原有逻辑 }
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

