使用 servlet 下载 zip 文件会返回损坏的 zip
我正在尝试使用 servlet 创建一个 zip 文件,但它返回了一个损坏的 zip 文件,这是我正在创建 zip 的 zipcontents 函数中的代码,有人可以帮助我吗?提前致谢。
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException,
IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
res.setContentType("application/zip");
res.setHeader("Content-Disposition", "attachment; filename=output.zip;");
fsep = File.separator;
rootDir = new File(getServletContext().getRealPath("Projects" + File.separator + "amrurta"));
File list[] = rootDir.listFiles();
zos = new ZipOutputStream(bout);
zipContents(list, rootDir.getName() + fsep);
zos.close();
res.getWriter().println(bout.toString());
}
public void zipContents(File[] file, String dir) {
// dir - directory in the zip file
byte[] buffer = new byte[4096];
try {
for (int i = 0; i < file.length; i++) { // zip files
if (file[i].isFile()) {
fis = new FileInputStream(file[i]);
zos.putNextEntry(new ZipEntry(dir + file[i].getName()));
// shows how its stored
// System.out.println(dir+file[i].getName());
int bytes_read;
while ((bytes_read = fis.read(buffer)) != -1)
zos.write(buffer, 0, bytes_read);
fis.close();
}
} // for
// create empty dir if theres no files inside
if (file.length == 1)
zos.putNextEntry(new ZipEntry(dir + fsep)); // this part is erroneous i think
for (int i = 0; i < file.length; i++) { // zip directories
if (file[i].isDirectory()) {
File subList[] = file[i].listFiles();
// for dir of varying depth
File unparsedDir = file[i];
String parsedDir = fsep + file[i].getName() + fsep; // last folder
while (!unparsedDir.getParentFile().getName().equals(rootDir.getName())) {
unparsedDir = file[i].getParentFile();
parsedDir = fsep + unparsedDir.getName() + parsedDir;
}
parsedDir = rootDir.getName() + parsedDir; // add input_output as root
zipContents(subList, parsedDir);
}
} // for
} catch (IOException ioex) {
ioex.printStackTrace();
}
}
I am trying to create a zip file using servlets but it returns me a corrupt zip file , here is the code for that in zipcontents function i am creating the zip , can someone help me out. Thanks in Advance.
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException,
IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
res.setContentType("application/zip");
res.setHeader("Content-Disposition", "attachment; filename=output.zip;");
fsep = File.separator;
rootDir = new File(getServletContext().getRealPath("Projects" + File.separator + "amrurta"));
File list[] = rootDir.listFiles();
zos = new ZipOutputStream(bout);
zipContents(list, rootDir.getName() + fsep);
zos.close();
res.getWriter().println(bout.toString());
}
public void zipContents(File[] file, String dir) {
// dir - directory in the zip file
byte[] buffer = new byte[4096];
try {
for (int i = 0; i < file.length; i++) { // zip files
if (file[i].isFile()) {
fis = new FileInputStream(file[i]);
zos.putNextEntry(new ZipEntry(dir + file[i].getName()));
// shows how its stored
// System.out.println(dir+file[i].getName());
int bytes_read;
while ((bytes_read = fis.read(buffer)) != -1)
zos.write(buffer, 0, bytes_read);
fis.close();
}
} // for
// create empty dir if theres no files inside
if (file.length == 1)
zos.putNextEntry(new ZipEntry(dir + fsep)); // this part is erroneous i think
for (int i = 0; i < file.length; i++) { // zip directories
if (file[i].isDirectory()) {
File subList[] = file[i].listFiles();
// for dir of varying depth
File unparsedDir = file[i];
String parsedDir = fsep + file[i].getName() + fsep; // last folder
while (!unparsedDir.getParentFile().getName().equals(rootDir.getName())) {
unparsedDir = file[i].getParentFile();
parsedDir = fsep + unparsedDir.getName() + parsedDir;
}
parsedDir = rootDir.getName() + parsedDir; // add input_output as root
zipContents(subList, parsedDir);
}
} // for
} catch (IOException ioex) {
ioex.printStackTrace();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
代码中的问题太多了。主要的问题有:
zos
被声明为 servlet 实例变量。这不是线程安全的。它在多个请求之间共享。您可能面临后续请求在未完成时覆盖前一个请求的风险。二进制 ZIP 内容通过
bout.toString()
转换为字符数据。这肯定会损坏二进制数据。您应该使用通常的InputStream#read()
/OutputStream#write()
循环将二进制数据写入为二进制数据。代码不会在每个条目结束时调用
zos.closeEntry()
。我认为#2是主要原因。您不需要
ByteArrayOutputStream
。这只是不必要的内存占用。只需将response.getOutputStream()
包装在ZipOutputStream
中即可。There are too much problems in the code. the major ones which springs in are:
The
zos
is been declared as servlet instance variable. This is not threadsafe. It is been shared among multiple requests. You risk that a subseuent request overwrites the previous one when it's not been finished.The binary ZIP content is been converted to character data with
bout.toString()
. This will definitely corrupt binary data. You should write binary data as binary data using the usualInputStream#read()
/OutputStream#write()
loop.The code does not call
zos.closeEntry()
at end of each entry.I think #2 is the major cause. You don't need
ByteArrayOutputStream
. It's only an unnecessary memory hog. Just wrap theresponse.getOutputStream()
inZipOutputStream
.另一种可能的原因是应用服务器的 JVM 版本和编译 servlet 的编译器不同。
非常罕见的问题,但很难理解。
One more possible reason is different JVM versions of application server and compiler which compiles servlet.
Very rare issue but veeery hard to understand.
你可以像这样创建
ZipOutputStream zipOut = new ZipOutputStream(res.getOutputStream());
您写入 zip 条目的每个 zip 条目都将流式传输回调用者。
You could create like this
ZipOutputStream zipOut = new ZipOutputStream(res. getOutputStream());
And every zip entry you write into zip entry will be streamed back to the caller.