If you have stored files in a path outside of the web container or in a database, then the client cannot access the files directly by a relative URI. A good practice is to create a Servlet which loads the file from a path outside of the web container or from a database and then streams the file to the HttpServletResponse. The client should get a 'Save as' popup dialogue, thanks to the Content-disposition header being set to attachment. You can pass the file name or the file ID as a part of the request URI. You can also consider to pass it as a request parameter, but that would cause problems with getting the filename right during saving in certain web browsers (Internet Explorer and so on).

Important note: this servlet example does not take the requested file as request parameter, but just as part of the absolute URL, because some browsers such as Internet Explorer would take the last part of the servlet URL path as filename during the 'Save As' dialogue instead of the in the headers supplied filename. Some browsers would also not be able to detect the correct content type and the associated application (yes, it ignores the Content-Type header as well!!). Using the filename as part of the absolute URL (and thus not as request parameter) will fix this stupid browser behaviour.

First prepare a DTO (Data Transfer Object) for File which can be used to hold information about the file (this is not the same as java.io.File! you may choose another name if this is too confusing). You can map this DTO to the database and use a DAO class to obtain it. You can get the file as InputStream from the database using ResultSet#getBinaryStream().

In the last example of an FileServlet serving from database, the ID is encrypted by MD5. It's your choice how you want to implement the use of ID, but keep in mind that plain numeric ID's like 1, 2, 3 and so on makes the hacker easy to guess for another files in the database, which they probably may not view at all. Then rather use a MD5 hash based on a combination of the numeric ID, the filename and the filesize for example. And last but not least, use PreparedStatement instead of a basic Statement to request the file by ID from database, otherwise you will risk an SQL injection when a hacker calls for example "file?id=';TRUNCATE TABLE File--".