views:

4779

answers:

2

I have a Servlet which is returning a csv file that is 'working' over HTTP in both internet explorer and firefox. When I execute the same Servlet over HTTPS only firefox continues to download the csv file over HTTPS. I don't think this is necessarily an Internet 6 or 7 issue described on MSDN :

The message is:

Internet Explorer cannot download data.csv from mydomain.com Internet Explorer was not able to open this Internet site. The requested site is either unavailable or cannot be found. Please try again later.

Please note that the site is still 'up' after this message and you can continue to browse the site, its just the download of the CSV that prompts this message. I have been able to access similar files over https on IE from other j2ee applications so I believe it is our code. Should we not be closing the bufferedOutputStream?

UPDATE

whether to close or not to close the output stream: I asked this question on the java posse forums and the discussion there is also insightful. In the end it seems that no container should rely on the 'client' (your servlet code in this case) to close this output stream. So if your failure to close the stream in your servlet causes a problem it is more a reflection on the poor implementation of your servlet container than your code. I sited the behavior of the IDEs and tutortials from Sun, Oracle and BEA and how they are also inconsistent in whether they close the stream or not.

About IE specific behavior: In our case a separate product 'Oracle Web Cache' was introducing the additional header values which impacts Internet explorer only because of the way IE implements the 'No Cache' requirement (see the MSDN article). The code is:

public class DownloadServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, 
                      HttpServletResponse response) throws ServletException, 
                                                           IOException {
        ServletOutputStream out = null;
        ByteArrayInputStream byteArrayInputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        try {
            response.setContentType("text/csv");
                        String disposition = "attachment; fileName=data.csv";
            response.setHeader("Content-Disposition", disposition);

            out = response.getOutputStream();
            byte[] blobData = dao.getCSV();

            //setup the input as the blob to write out to the client
            byteArrayInputStream = new ByteArrayInputStream(blobData);
            bufferedOutputStream = new BufferedOutputStream(out);
            int length = blobData.length;
            response.setContentLength(length);
            //byte[] buff = new byte[length];
             byte[] buff = new byte[(1024 * 1024) * 2];

            //now lets shove the data down
            int bytesRead;
            // Simple read/write loop.
            while (-1 != 
                   (bytesRead = byteArrayInputStream.read(buff, 0, buff.length))) {
                bufferedOutputStream.write(buff, 0, bytesRead);
            }
            out.flush();
            out.close();

        } catch (Exception e) {
            System.err.println(e); throw e;

        } finally {
            if (out != null)
                out.close();
            if (byteArrayInputStream != null) {
                byteArrayInputStream.close();
            }
            if (bufferedOutputStream != null) {
                bufferedOutputStream.close();
            }
        }
    }
+1  A: 

I am really confused about your "from back through the breast into the head" write mechanism. Why not simple (the servlet output stream will be bufferend, thats container stuff):

byte[] csv = dao.getCSV();
response.setContentType("text/csv");
response.setHeader("Content-Disposition", "attachment; filename=data.csv"));
reponse.setContentLength(csv.length);
ServletOutputStream out = response.getOutputStream();
out.write(csv);

There should also be no need to flush the output stream nor to close.

The header content should not be parsed case sensitive by IE, but who knows: do not camelcase fileName. The next question is the encoding. CSV is text, so you should use getWriter() instead or getOutputStream() and set the content type to "text/csv; charset=UTF-8" for example. But the dao should provide the CSV as String instead of byte[].

The servlet code has nothing to d with HTTPS, so the protocol does not matter from the server side. You may test the servlet from localhost with HTTP i hope.

What about filters in your application? A filter may als set an HTTP header (or as footer) with cache-control for example.

Arne Burmeister
It does work in HTTP and HTTPS in most browsers with the exception of internet explorer with HTTPS (same message indicated in the MSDN article I linked to). I note in your example you are not doing an out.close(). I am looking for confirmation that the Servlet should not be calling out.close() as well.
Brian
I agree that the stuff with buffered IO is unnecessary. Don't do that. Similarly, I don't think that closing the stream is necessary.
jsight
+1  A: 

I've had a similar problem with downloads over https://. I solved it by setting the appropriate headers to allow Internet Explorer to cache the download.

So, set 'pragma: ' to empty or public and make sure you have no 'Cache-Control: ' header or set that appropriately too.

(I found this solution here: http://www.danforys.com/2008/08/06/unsetting-http-headers-in-php/ ).

warpr
Brian