Implementing a simple file download servlet [duplicate]
Asked Answered
A

5

46

How should I implement simple file download servlet?

The idea is that with the GET request index.jsp?filename=file.txt, the user can download for example. file.txt from the file servlet and the file servlet would upload that file to user.

I am able to get the file, but how can I implement file download?

Annal answered 18/9, 2009 at 6:41 Comment(1)
You should give serious consideration to the security risks involved. See for example owasp.org/index.php/Path_TraversalDickenson
C
57

That depends. If said file is publicly available via your HTTP server or servlet container you can simply redirect to via response.sendRedirect().

If it's not, you'll need to manually copy it to response output stream:

OutputStream out = response.getOutputStream();
FileInputStream in = new FileInputStream(my_file);
byte[] buffer = new byte[4096];
int length;
while ((length = in.read(buffer)) > 0){
    out.write(buffer, 0, length);
}
in.close();
out.flush();

You'll need to handle the appropriate exceptions, of course.

Catechumen answered 18/9, 2009 at 6:51 Comment(8)
what's about content-disposition and content-type?Mcnamara
Note that the "read" while condition should use -1 and not 0: while ((length = in.read(buffer)) > -1)Selfdriven
@Selfdriven 0 works just as well. If the number of bytes read was 0, there's nothing to write to output.Catechumen
@Catechumen The doc says -1 if there is no more data because the end of the file has been reached. Why take the risk? maybe the disk is unavailable for a second so you get 0 but suppose to continue reading?Selfdriven
@Selfdriven Perhaps you should read the documentation fully before you quote it here. Pay special attention to phrases like "This method blocks until input data is available" and "at least one byte is read and stored".Catechumen
Does this have performance or scalability implications, since a Thread in the application is kept open to allow this download ? Is there a more robust approach, while allowing the link to be only available to this user ?Automaton
I am new to java - what is the meaning of new byte[4096] is that 4MB memory allocationGully
@VeshrajJoshi, 4096 BYTES ~ 4KBBasilio
S
66

Assuming you have access to servlet as below

http://localhost:8080/myapp/download?id=7

I need to create a servlet and register it to web.xml

web.xml

<servlet>
     <servlet-name>DownloadServlet</servlet-name>
     <servlet-class>com.myapp.servlet.DownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
     <servlet-name>DownloadServlet</servlet-name>
     <url-pattern>/download</url-pattern>
</servlet-mapping>

DownloadServlet.java

public class DownloadServlet extends HttpServlet {


    protected void doGet( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

         String id = request.getParameter("id");

         String fileName = "";
         String fileType = "";
         // Find this file id in database to get file name, and file type

         // You must tell the browser the file type you are going to send
         // for example application/pdf, text/plain, text/html, image/jpg
         response.setContentType(fileType);

         // Make sure to show the download dialog
         response.setHeader("Content-disposition","attachment; filename=yourcustomfilename.pdf");

         // Assume file name is retrieved from database
         // For example D:\\file\\test.pdf

         File my_file = new File(fileName);

         // This should send the file to browser
         OutputStream out = response.getOutputStream();
         FileInputStream in = new FileInputStream(my_file);
         byte[] buffer = new byte[4096];
         int length;
         while ((length = in.read(buffer)) > 0){
            out.write(buffer, 0, length);
         }
         in.close();
         out.flush();
    }
}
Segmentation answered 11/1, 2013 at 15:16 Comment(3)
Note that the "read" while condition should use -1 and not 0: while ((length = in.read(buffer)) > -1)Selfdriven
Use annotation @WebServlet for simplify the code @WebServlet(description = "Download Servlet", urlPatterns = {"/download"})Merline
If the ID parameter is not a hash, an attacker could try to guess other IDs and get files from your database. Also, it is good to validate that the received ID does not contain any SQL Injection.Loriannlorianna
C
57

That depends. If said file is publicly available via your HTTP server or servlet container you can simply redirect to via response.sendRedirect().

If it's not, you'll need to manually copy it to response output stream:

OutputStream out = response.getOutputStream();
FileInputStream in = new FileInputStream(my_file);
byte[] buffer = new byte[4096];
int length;
while ((length = in.read(buffer)) > 0){
    out.write(buffer, 0, length);
}
in.close();
out.flush();

You'll need to handle the appropriate exceptions, of course.

Catechumen answered 18/9, 2009 at 6:51 Comment(8)
what's about content-disposition and content-type?Mcnamara
Note that the "read" while condition should use -1 and not 0: while ((length = in.read(buffer)) > -1)Selfdriven
@Selfdriven 0 works just as well. If the number of bytes read was 0, there's nothing to write to output.Catechumen
@Catechumen The doc says -1 if there is no more data because the end of the file has been reached. Why take the risk? maybe the disk is unavailable for a second so you get 0 but suppose to continue reading?Selfdriven
@Selfdriven Perhaps you should read the documentation fully before you quote it here. Pay special attention to phrases like "This method blocks until input data is available" and "at least one byte is read and stored".Catechumen
Does this have performance or scalability implications, since a Thread in the application is kept open to allow this download ? Is there a more robust approach, while allowing the link to be only available to this user ?Automaton
I am new to java - what is the meaning of new byte[4096] is that 4MB memory allocationGully
@VeshrajJoshi, 4096 BYTES ~ 4KBBasilio
E
11

Try with Resource

File file = new File("Foo.txt");
try (PrintStream ps = new PrintStream(file)) {
   ps.println("Bar");
}
response.setContentType("application/octet-stream");
response.setContentLength((int) file.length());
response.setHeader( "Content-Disposition",
         String.format("attachment; filename=\"%s\"", file.getName()));

OutputStream out = response.getOutputStream();
try (FileInputStream in = new FileInputStream(file)) {
    byte[] buffer = new byte[4096];
    int length;
    while ((length = in.read(buffer)) > 0) {
        out.write(buffer, 0, length);
    }
}
out.flush();
Embark answered 27/3, 2015 at 19:37 Comment(2)
"out" must be inside try() block too. And so out.flush().Proconsul
this approach is really useful when you need to download a big file (more than 5MB for example). If you don't specify the content length the client could close the TCP connection with your server, and then you get a SocketException: Connection reset by peer: socket write errorUlrike
W
2

The easiest way to implement the download is that you direct users to the file location, browsers will do that for you automatically.

You can easily achieve it through:

HttpServletResponse.sendRedirect()
Weatherman answered 18/9, 2009 at 6:52 Comment(0)
E
0

And to send a largFile

byte[] pdfData = getPDFData();

String fileType = "";

res.setContentType("application/pdf");

httpRes.setContentType("application/.pdf");
httpRes.addHeader("Content-Disposition", "attachment; filename=IDCards.pdf");
httpRes.setStatus(HttpServletResponse.SC_OK);
OutputStream out = res.getOutputStream();
System.out.println(pdfData.length);
         
out.write(pdfData);
System.out.println("sendDone");
out.flush();
Elative answered 12/7, 2020 at 11:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.