Force downloading a file using Spring MVC and a self-implemented interceptor produces a weird issue
-
02-07-2021 - |
Pergunta
All of my controllers extend the following abstract class:
public abstract class AbstractController {
public HttpServletRequest request;
public HttpServletResponse response;
public ModelMap model;
}
Moreover, I implemented the following interceptor:
public class HttpRequestInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException {
if (handler instanceof AbstractController) {
AbstractController controller = (AbstractController) handler;
controller.request = request;
controller.response = response;
controller.model = new ModelMap();
}
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
if (handler instanceof AbstractController && modelAndView != null) {
AbstractController controller = (AbstractController) handler;
modelAndView.addAllObjects(controller.model);
}
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
This is a solution I found to improve the factorization of my code, since you won't need to pass the request
, the response
and the model
as method parameters within your controllers. The solution works fine, until I found this issue:
public class HomeController extends AbstractController {
@RequestMapping
public void download1() {
// use the parent attribute response
File file = new File(MY_FILE_PATH);
InputStream in = new BufferedInputStream(new FileInputStream(file));
ServletOutputStream out = response.getOutputStream();
IOUtils.copy(in, out);
response.flushBuffer();
}
@RequestMapping
public void download2(HttpServletResponse response) {
// use the response passed as parameter
File file = new File(MY_FILE_PATH);
InputStream in = new BufferedInputStream(new FileInputStream(file));
ServletOutputStream out = response.getOutputStream();
IOUtils.copy(in, out);
response.flushBuffer();
}
}
Both of the two methods above make the browser downloading a file, but the download1
one generated an empty file while the download2
generates the original file as it should. Any idea why?
Thanks to the debugger, I noticed that in the postHandle
method of the interceptor, the download2
method generates a modelAndView
which equals null
, while the download1
one generated an instanciated one. This should mean something for the issue, but I can't find what.
How get a response
instanciated when passed as a parameter of a controller's method?
Solução
Don't do this :
public abstract class AbstractController {
public HttpServletRequest request;
public HttpServletResponse response;
public ModelMap model;
}
Instance variables in controllers (which have default scope of singleton btw) is a bad idea.
Outras dicas
Just make something like this (to a txt file):
@RequestMapping(value="/download", method=RequestMethod.GET, produces=MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody
public String download(HttpServletResponse response) throws IOException {
response.setContentType("application/force-download");
FileReader fr = new FileReader("/folder/file.extension");
return IOUtils.toString(fr); // IOUtils come from Apache Commons IO
}