In Spring MVC application, to download a resource such as a file to the browser, you need to do the following in your controller.
- Use the
void
return type for your request-handling method and addHttpServletResponse
as an argument to the method. - Set the response’s content type to the file’s content type. If you do not know what the content type is or want the browser to always display the
Save As
dialog, set it toAPPLICATION/OCTET-STREAM
(NOT case sensitive). - Add an HTTP response header named Content-Disposition and give it the value attachment; filename=fileName, where
fileName
is the default file name that should appear in the File Download dialog box.
1. Sping MVC File Download Controller
Let’s look at an example implementation for file download controller.
@Controller @RequestMapping ( "/download" ) public class FileDownloadController { @RequestMapping ( "/pdf/{fileName:.+}" ) public void downloadPDFResource( HttpServletRequest request, HttpServletResponse response, @PathVariable ( "fileName" ) String fileName) { //If user is not authorized - he should be thrown out from here itself //Authorized user will download the file String dataDirectory = request.getServletContext().getRealPath( "/WEB-INF/downloads/pdf/" ); Path file = Paths.get(dataDirectory, fileName); if (Files.exists(file)) { response.setContentType( "application/pdf" ); response.addHeader( "Content-Disposition" , "attachment; filename=" +fileName); try { Files.copy(file, response.getOutputStream()); response.getOutputStream().flush(); } catch (IOException ex) { ex.printStackTrace(); } } } } |
If you want to enable downloads for only authorized users then check the user’s logged in status first in the method, and then allow download otherwise redirect him to login screen.
Now if you hit the application URL :
http://localhost:8080/springmvcexample/download/pdf/sample.pdf
, you will be able to get the Save As dialog box in your browser like below:
The file is placed inside folder “/WEB-INF/downloads/pdf”. You are free to change the path – make sure you change the controller code as well.
2. Prevent Cross-Referencing of File Downloads
Many times, other websites may cross reference your files in their websites as direct links. You may not want to allow it. To disallow all download requests, coming from other domains, you can check if the referer header contains your domain name.
Our modified
FileDownloadController
will send files to the browser, only if the referer header is not null. This will prevent the images from being downloaded directly by typing their URLs in the browser or request coming from other domains.@Controller @RequestMapping ( "/download" ) public class FileDownloadController { @RequestMapping ( "/pdf/{fileName:.+}" ) public void downloadPDFResource( HttpServletRequest request, HttpServletResponse response, @PathVariable ( "fileName" ) String fileName, @RequestHeader String referer) { //Check the renderer if (referer != null && !referer.isEmpty()) { //do nothing //or send error } //If user is not authorized - he should be thrown out from here itself //Authorized user will download the file String dataDirectory = request.getServletContext().getRealPath( "/WEB-INF/downloads/pdf/" ); Path file = Paths.get(dataDirectory, fileName); if (Files.exists(file)) { response.setContentType( "application/pdf" ); response.addHeader( "Content-Disposition" , "attachment; filename=" +fileName); try { Files.copy(file, response.getOutputStream()); response.getOutputStream().flush(); } catch (IOException ex) { ex.printStackTrace(); } } } } |
Now if you try to hit the URL from browser directly, you will get this error:
java.lang.IllegalStateException: Missing header 'referer' of type [java.lang.String] at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.raiseMissingHeaderException(HandlerMethodInvoker.java: 797 ) at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveRequestHeader(HandlerMethodInvoker.java: 566 ) at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java: 355 ) at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java: 172 ) at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java: 446 ) |
Drop me your questions in comments section.
No comments:
Post a Comment