-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Léa,

On 9/25/14 5:48 AM, Léa Massiot wrote:
> My question is about downloading a file using a servlet and
> handling possible errors that may take place during the download
> process.
> 
> 1) I have a JSP page "download-file.jsp" with a "Download file"
> button: ====================================================== 
> <form  method="post" action="do_download_file"> [...] <input
> type="submit" name="download_file" id="download_file" 
> value="Download file" /> [...] </form> 
> ====================================================== When this
> button is hit by a user, the "doPost()" method of the servlet 
> mapped with the action "do_download_file" is executed. It includes
> a call to the method below.
> 
> 2) Everywhere on the Web one can find that type of code (for the
> file downloading): 
> ====================================================== public
> boolean downloadFile(String s_fileNameAndPath, String s_fileName, 
> HttpServletResponse httpServletResponse) throws IOException,
> SQLException, ClassNotFoundException, ModelException { File file =
> null; long l_fileLength = -1;

Oh, man. I hate Hungarian Notation. :/

> ServletOutputStream servletOutputStream = null; int n_bufsize =
> 4096; DataInputStream dataInputStream = null; int n_nbBytesRead =
> -1;

Why bother initializing all these local variables to default values?
Why not just declare them and then set them later? Giving them default
values doesn't gain anything and takes time during the request that
could be used to actually accomplish something. I'd remove the default
values.

> boolean b_anErrorOccurred = false;

Don't want to perform exception handling? This looks like C code...

> byte[] byteBuffer;  file = new File(s_fileNameAndPath);  
> l_fileLength = file.length();  if(l_fileLength <= 0) { 
> b_anErrorOccurred = true; return b_anErrorOccurred; }

It's an error to have a file with zero bytes? Interesting.

> n_nbBytesRead = 0; byteBuffer = new byte[n_bufsize];  
> servletOutputStream = httpServletResponse.getOutputStream();  
> httpServletResponse.setContentType("text/html");

This is probably a mistake: what if it's a binary file?

> httpServletResponse.setContentLength((int) l_fileLength);

This is a mistake: l_filelength can be greater than Integer.MAX_VALUE.
Unfortunately, the Servlet API designers made this mistake long ago
and it can't be undone. Use response.setHeader("Content-Length",
String.valueOf(l_fileLength)) instead.

> httpServletResponse.setHeader("Content-Disposition", "attachment; 
> filename=\"" + s_fileName + "\"");  dataInputStream = new
> DataInputStream(new FileInputStream(file));

Wait, what? Why are you using a DataInputStream? A plain-old
FileInputStream will work just fine. DataInputStream is for reading
structured values from files... you just want to read bytes. Ditch the
DataInputStream.

> while((dataInputStream != null) && ((n_nbBytesRead =
> dataInputStream.read(byteBuffer)) != -1)) { 
> servletOutputStream.write(byteBuffer, 0, n_nbBytesRead); 
> servletOutputStream.flush();

Don't flush: buffering is your friend.

> }  if(true) { throw new ModelException("To provoke an error on
> purpose."); }  dataInputStream.close(); 
> servletOutputStream.close();

You need these close() calls in a finally block.

> return b_anErrorOccurred;

Don't return an error code; throw an exception instead.

> ====================================================== (Apart from
> the exception being thrown) this code works: it actually downloads
> the targeted file.
> 
> You may have noticed that in the method above, I deliberately throw
> an exception so that I can test error handling.
> 
> My problem is that, with this way of doing things, I can't tell the
> user that the download operation failed since the
> "HttpServletResponse" has already been "consumed" when the method
> returns...

The phrase you are looking for is that the response "has been
committed". That means that the headers and probably part of the
response body have already been put onto the wire and you simply can't
take them back.

> My question is: Ideally, after the download, I wish I could go back
> to the JSP "download-file.jsp" so that I can tell the user that an
> error occurred while downloading the file.

At some point, there is no way to go back. If you were
dynamically-generating the content and wanted to make sure that the
content completed successfully, you'd buffer the whole response just
to be sure. In this case, the response is already buffered (in a way
- -- it's on the disk!) so there's really nothing more you can do.

I suppose you could read the whole file into memory to ensure you
wouldn't get any read errors during the download, but then you'd need
to have huge buffers for huge files. That's a bad plan for a web
application.

> If the download was successful, I also would like to tell the user
> that everything went right...

You can't do this, either. But guess what? The user will already know
if it was successful: they will have either received Content-Length
bytes or the connection would have failed. There's no need to remind
them that the download completed successfully.

> I don't see how I can both download the file and redirect the user
> towards the "download-file.jsp" page with an appropriate message. 
> Do you have an idea how I should proceed?

What you are asking is not really possible.

If you want some inspiration, read the code for the DefaultServlet.

- -chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
Comment: GPGTools - http://gpgtools.org

iQIcBAEBCAAGBQJUJBPpAAoJEBzwKT+lPKRYEFEQAK0sQcHmg4WHt8rXbrRHDNZz
Hd0IcLyxJ7BtjjScx0fhcu/EOIEsGXPN2VOEIsOsapk1Y/Io5uaR/1Yo2FfSLEHk
EujSOLwkj0bx7fIRRG6IygQOSGrTWdBMtlYHcbUVbkVs2BfnM+eSw6I9GIpqiKtG
PtftRzSX2qP00K8HldoMULTQc38HRVNKKuMZE3JWmD4dSZW/uWWs7lEugrGH5ZY3
d2mjlW+mYkG7tp7ATe1Kw1qR8aaeQdHkdx369btufIYyTLYRtKzS6v8QIQ12xL+Q
nCF1QVOnbzdQr6r6ZH/Iu5xqjXStpH/lxZF89dV43FAQMCwAnW1Uiz7yBS5ancWL
zO6kVJxsvzS+fMUFy6uziByz/dH5BUuLKeOpQJ8cksJ0J2ZxDQom8LoB/UQyeLaA
qrOsHqAwQSulPW7bmh16xekqpwTqfCP8HqgC+EKQePI5a5UpJbtml+fTRIZByp7/
Gk1qM668is3ilwmpLUIrynJr3TzklkBKZ3LN4dQ0Sdshrz42F/zPT/lO3DcN3tN7
rpUrG/KrCI2/YLsKP1qS/dMzvoTx8ArS7A2ET1pkMnP7SZ1i86Vims4dtjGzRulS
h4dHt2Wl+uBm82iexyybOcvhraBXOkWFlbzRShqEQJkofDtE88iOBx/Kuz7VzJWR
hYYfeGpfPF5QJiGlXQmH
=edYK
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to