views:

694

answers:

7

I'm having a problem trying to serve a zip file in a JSP.

The zip file is always corrupt after it has finished downloading. I've tried a few different methods for reading and writing, and none of them seem to do the trick.

I figure it is probably adding in ascii characters somewhere as the file will open and display all the filenames, but I can't extract any files.

Here's my latest code:

<%@ page import= "java.io.*" %>

<% 
    BufferedReader bufferedReader = null;

    String zipLocation = "C:\\zipfile.zip"; 

    try
    {
     bufferedReader = new BufferedReader(new FileReader(zipLocation));
     response.setContentType("application/zip");
     response.setHeader( "Content-Disposition", "attachment; filename=zipfile.zip" );

     int anInt = 0;
     while((anInt = bufferedReader.read()) != -1)
     {
      out.write(anInt);
     }
    }
    catch(Exception e)
    {
     e.printStackTrace();
    }
%>

EDIT: I moved the code to a servlet and it still didn't work. I changed a bunch more stuff around, so here's the latest non-working code:

public void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
{
 try
 {
  String templateLocation = Config.getInstance().getString("Site.templateDirectory");

  response.setContentType("application/zip");
  response.setHeader("Content-Disposition", "attachment; filename=output.zip;");

  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  BufferedOutputStream bos = new BufferedOutputStream(baos);
  FileInputStream fis = new FileInputStream(templateLocation);

  int len;
  byte[] buf = new byte[1024];

  while ((len = fis.read(buf)) > 0)
  {
   bos.write(buf, 0, len);
  }

  bos.close();
  PrintWriter pr = response.getWriter();
  pr.write(baos.toString());
  pr.close();
 }
 catch (Exception e)
 {
  e.printStackTrace();
 }
}

EDIT2:

This is the servlet code that I actually works. Thank you to everyone!

public void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
{
 try
 {
  String templateLocation = Config.getInstance().getString("Site.templateDirectory");

  response.setContentType("application/zip");
  response.setHeader("Content-Disposition", "attachment; filename=output.zip;");

  BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
  FileInputStream fis = new FileInputStream(templateLocation);

  int len;
  byte[] buf = new byte[1024];

  while ((len = fis.read(buf)) > 0)
  {
   bos.write(buf, 0, len);
  }

  bos.close();
 }
 catch (Exception e)
 {
  e.printStackTrace();
 }
}
+4  A: 

The JSP adds whitespace to the output. I suggest you move this to a servlet.

Alternatively, you can take a look at http://stackoverflow.com/questions/208736/strip-whitespace-from-jsp-output , but I'm not sure that it would not affect the ZIP output itself.

Robert Munteanu
+4  A: 

Zips are binary files, and therefore not suitable for being transferred as character data. Also text outside the code may corrupt the file.

Use a plain, vanilla servlet instead of the JSP.

Tom Hawtin - tackline
And, for heaven's sake, do not use a Reader to process binary Data.
Michael Borgwardt
A: 

Readers are typically for character streams if I remember correctly - you might try something more along the lines of a ByteArrayOutputStream? Also, the JSP could indeed be corrupting the output as others have commented.

Jon
I've tried using streams and readers, and I get the same result. I'll try again with a plain, vanilla servlet, I guess.
Jesse
I don't know if you found this already but Sun's forums had a similar thread to offer: http://forums.sun.com/thread.jspa?threadID=5266941
Jon
+2  A: 

The problem in this exact snippet is on line two - you have a blank line outside JSP tags, which is sent to the browser as HTML (i.e. a blank line in HTML source).

Be very careful to remove anything that isn't in <% %>, especially whitespace (even a terminal newline!), or follow the advice elsewhere and use a servlet :)

Jeremy Smyth
Removing all white space did not work also. I figured since I set the header to attachment, it would ignore the white space.
Jesse
A: 
bufferedReader = new BufferedReader(new FileReader(zipLocation));

Apart from the Servlet/JSP issue, this line is corrupting your data with 100% certainty. It will try to interpret the binary data as text using the default platform encoding, which means that about half the bytes in the file are replaced with "unknown character".

Bytes and strings are not the same thing!

Michael Borgwardt
A: 

I would also suggest to use a plain servlet and would consider Robert Munteanu's answer as right, but you could do this in your JSP also. The Problem isn't the added whitespace, but that the implicit variable "out" is a instance of JSPWriter and a Writer is for characters, not for bytes. Try to use response.getOutputStream() and right on it, that should work. You will get an Exception saying someone else already got the OutputStream, but you can ignore it.

But as I said, using a Servlet would be much cleaner.

Tim Büthe
I've added my servlet code to the question as I still can't get it to work.
Jesse
+1  A: 

Your servlet code does not work because you are outputting your stream to response.getWriter(), which as others have pointed out is for character data. I quote the Javadoc:

PrintWriter getWriter() throws IOException
Returns a PrintWriter object that can send character text to the client. The PrintWriter uses the character encoding returned by getCharacterEncoding()

Not only that, but you aren't even writing the byte array to the Writer, you are writing the results of ByteArrayOutputStream.toString(), which also does a character conversion~

You want to use response.getOutputStream(), something like:

BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());

And then write the byte contents of your file to it.

matt b