9

This might sound like a trivial question, but after hours of searching I am yet to find an answer to this. The problem, as far as I understand, is that I am trying to return a FileSystemResource from the controller and Thymeleaf expects me to supply a String resource using which it will render the next page. But since I am returning a FileSystemResource, I get the following error:

org.thymeleaf.exceptions.TemplateInputException: Error resolving template "products/download", template might not exist or might not be accessible by any of the configured Template Resolvers

The controller mapping I have used is:

@RequestMapping(value="/products/download", method=RequestMethod.GET)
public FileSystemResource downloadFile(@Param(value="id") Long id) {
    Product product = productRepo.findOne(id);
    return new FileSystemResource(new File(product.getFileUrl()));
}

My HTML link looks something like this:

<a th:href="${'products/download?id=' + product.id}"><span th:text="${product.name}"></span></a>

I don't want to be redirected anywhere, I just need the file downloaded once the link is clicked. Is this actually the right implementation? I'm not sure.

shyam
  • 1,348
  • 4
  • 19
  • 37
  • `` the `$` in the beginning should be `@`, can you try that way? – mtyurt Apr 23 '15 at 07:35
  • If I use `@` the `product.id` does not get replaced with the actual product id. – shyam Apr 23 '15 at 07:38
  • Because it should be `th:href="@{'products/download?id=' + ${product.id}}"` as stated in the [documentation](http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#link-urls) – mtyurt Apr 23 '15 at 07:44
  • Yeah. But the error remains. The actual id is showing up on the get request now though. – shyam Apr 23 '15 at 07:59
  • Now your problem is about Spring. Check this question: http://stackoverflow.com/questions/5673260/downloading-a-file-from-spring-controllers – mtyurt Apr 23 '15 at 08:06
  • The second answer on that question is exactly what I have done as far as I can see. And, IMHO, I think the problem is the fact that the url on the address bar changes when you click the link. And, as far as Thymeleaf is concerned, it doesn't have a html template relating to that url, hence throwing the error. – shyam Apr 23 '15 at 08:27
  • Are you including the @ResponseBody annotation? – Leandro Carracedo Apr 23 '15 at 14:57
  • 1
    @PatrickLC That did the trick! If you put that up as the answer I shall accept it. – shyam Apr 23 '15 at 15:27

5 Answers5

14

You need to change the th:href to look like:

<a th:href="@{|/products/download?id=${product.id}|}"><span th:text="${product.name}"></span></a>

Then also you need to change your controller and include the @ResponseBody annotation:

@RequestMapping(value="/products/download", method=RequestMethod.GET)
@ResponseBody
public FileSystemResource downloadFile(@Param(value="id") Long id) {
    Product product = productRepo.findOne(id);
    return new FileSystemResource(new File(product.getFileUrl()));
}
Leandro Carracedo
  • 7,233
  • 2
  • 44
  • 50
  • Hey. Just now I noticed that you have the `|` symbol instead of the usual `'`. Anything I should know of? – shyam Apr 23 '15 at 15:48
  • 4
    It's just another way to concatenate but easier than using '. Check this answer: http://stackoverflow.com/a/22064656/4316870 – Leandro Carracedo Apr 23 '15 at 15:52
5

Have a href url like

<a th:href="@{|/download?id=${obj.id}|}">download</a>

Then In controller define like below to write the file to the response object.

@RequestMapping(value="/download", method=RequestMethod.GET)
public void downloadFile(@Param(value="id") Long id,HttpServletResponse response) {
        try {
            Ticket ticket = this.findById(id);
        String fileName =  Paths.get(property.getFileLocation()).resolve(ticket.getFilePath()).toString();
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        String headerKey = "Content-Disposition";
        String headerValue = String.format("attachment; filename=\"%s\"", fileName);
        response.setHeader(headerKey, headerValue);
        FileInputStream inputStream;
        try {
            inputStream = new FileInputStream(fileName);
            try {
                int c;
                while ((c = inputStream.read()) != -1) {
                response.getWriter().write(c);
                }
            } finally {
                if (inputStream != null)
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    response.getWriter().close();
            }
        } catch (IOException e) {
            e.printStackTrace();

        }
    }
0

Thymeleaf is very well documented, you can find about literal substitutions (what Leandro showed above) in the manual - section 4.7

Excerpt from the manual

<span th:text="|Welcome to our application, ${user.name}!|">
Andrei Epure
  • 1,746
  • 1
  • 21
  • 30
0

for me, the answer from @leandro-carracedo worked but the browser would just list the file contents, not download.

This fixed that:

<a download="results.csv" th:href=... </a>
Vetras
  • 1,609
  • 22
  • 40
0

Thymeleaf <td><a th:href="${fileDetail.MsgFileName}" th:text="${fileDetail.MsgFileName}" /></td>.URL prepared at server end and href created at client side