Groovy下载含JSON的ZIP文件并解压的问题求助
Groovy 下载并解压ZIP文件的正确实现
问题根源
你的代码存在三个核心问题:
- 错误使用字符流读取二进制ZIP数据:
BufferedReader和InputStreamReader是用于处理文本的字符流,用它们读取ZIP这类二进制文件时,会将原始字节转换为UTF-8字符,直接破坏ZIP的二进制结构,导致后续无法正确解压。 - 误解响应格式:响应本身就是原始的ZIP二进制数据,并非Base64编码字符串,所以调用
decodeBase64()会抛出"bad character in base64 value"错误。 - 冗余的输出流操作:如果是GET请求获取ZIP,无需向
HttpURLConnection的输出流写入内容,这部分代码完全多余。
修正后的实现
1. 正确下载ZIP文件
以下提供两种下载方式,按需选择:
下载到本地文件
try { URL url = new URL(testUrl) HttpURLConnection conn = (HttpURLConnection) url.openConnection() conn.setRequestProperty("Authorization", "Bearer " + testPassword) conn.setRequestMethod("GET") // 明确指定GET请求 // 用字节流读取响应并写入本地文件 FileOutputStream fos = new FileOutputStream("downloaded.zip") byte[] buffer = new byte[4096] int bytesRead InputStream is = conn.getInputStream() while ((bytesRead = is.read(buffer)) != -1) { fos.write(buffer, 0, bytesRead) } // 关闭资源 fos.close() is.close() conn.disconnect() println("ZIP文件下载完成") } catch (Exception e) { println("下载错误: ${e.getMessage()}") e.printStackTrace() }
读取到内存字节数组
try { URL url = new URL(testUrl) HttpURLConnection conn = (HttpURLConnection) url.openConnection() conn.setRequestProperty("Authorization", "Bearer " + testPassword) conn.setRequestMethod("GET") ByteArrayOutputStream baos = new ByteArrayOutputStream() byte[] buffer = new byte[4096] int bytesRead InputStream is = conn.getInputStream() while ((bytesRead = is.read(buffer)) != -1) { baos.write(buffer, 0, bytesRead) } byte[] zipBytes = baos.toByteArray() // 关闭资源 baos.close() is.close() conn.disconnect() println("ZIP数据已读取到内存") } catch (Exception e) { println("读取错误: ${e.getMessage()}") e.printStackTrace() }
2. 解压ZIP文件
不管是本地ZIP文件还是内存中的字节数组,都可以用ZipInputStream来解压:
解压本地ZIP文件到指定目录
def unzipFile(String zipPath, String destDir) { File destDirectory = new File(destDir) if (!destDirectory.exists()) { destDirectory.mkdirs() } ZipInputStream zis = new ZipInputStream(new FileInputStream(zipPath)) ZipEntry zipEntry = zis.getNextEntry() while (zipEntry != null) { File newFile = newFile(destDirectory, zipEntry) if (zipEntry.isDirectory()) { if (!newFile.isDirectory() && !newFile.mkdirs()) { throw new IOException("无法创建目录: ${newFile.getAbsolutePath()}") } } else { // 创建父目录 File parent = newFile.getParentFile() if (!parent.isDirectory() && !parent.mkdirs()) { throw new IOException("无法创建父目录: ${parent.getAbsolutePath()}") } // 写入文件内容 FileOutputStream fos = new FileOutputStream(newFile) byte[] buffer = new byte[4096] int len while ((len = zis.read(buffer)) > 0) { fos.write(buffer, 0, len) } fos.close() } zipEntry = zis.getNextEntry() } zis.closeEntry() zis.close() } // 防止ZIP路径遍历攻击 private File newFile(File destinationDir, ZipEntry zipEntry) throws IOException { File destFile = new File(destinationDir, zipEntry.getName()) String destDirPath = destinationDir.getCanonicalPath() String destFilePath = destFile.getCanonicalPath() if (!destFilePath.startsWith(destDirPath + File.separator)) { throw new IOException("ZIP条目路径非法: ${zipEntry.getName()}") } return destFile } // 调用示例 unzipFile("downloaded.zip", "./unzip-dir")
解压内存中的ZIP字节数组到指定目录
def unzipBytes(byte[] zipBytes, String destDir) { File destDirectory = new File(destDir) if (!destDirectory.exists()) { destDirectory.mkdirs() } ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes)) ZipEntry zipEntry = zis.getNextEntry() while (zipEntry != null) { File newFile = newFile(destDirectory, zipEntry) if (zipEntry.isDirectory()) { if (!newFile.isDirectory() && !newFile.mkdirs()) { throw new IOException("无法创建目录: ${newFile.getAbsolutePath()}") } } else { File parent = newFile.getParentFile() if (!parent.isDirectory() && !parent.mkdirs()) { throw new IOException("无法创建父目录: ${parent.getAbsolutePath()}") } FileOutputStream fos = new FileOutputStream(newFile) byte[] buffer = new byte[4096] int len while ((len = zis.read(buffer)) > 0) { fos.write(buffer, 0, len) } fos.close() } zipEntry = zis.getNextEntry() } zis.closeEntry() zis.close() } // 调用示例(假设zipBytes是之前读取到的字节数组) unzipBytes(zipBytes, "./unzip-dir")
关键注意事项
- 始终用字节流处理二进制文件:ZIP、图片、视频等二进制数据必须用
InputStream/OutputStream这类字节流操作,不能用字符流(Reader/Writer)。 - 防范路径遍历风险:解压时必须校验ZIP条目的路径,防止恶意ZIP文件写入到目标目录外的位置(上述代码已通过
newFile方法处理该风险)。 - 及时关闭资源:确保所有流和连接都正确关闭,避免资源泄漏。
内容的提问来源于stack exchange,提问作者n1c9
相关产品推荐
相关产品推荐

