使用Spring Boot和Thymeleaf创建文件下载链接

9

这可能听起来像是一个琐碎的问题,但是经过数小时的搜索,我仍然找不到答案。据我所知,问题在于我试图从控制器返回一个 FileSystemResource ,而Thymeleaf期望我提供一个String资源,以便它将呈现下一页。但由于我正在返回一个FileSystemResource,因此我收到以下错误:

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

我使用的控制器映射是:

@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()));
}

我的HTML链接看起来像这样:
<a th:href="${'products/download?id=' + product.id}"><span th:text="${product.name}"></span></a>

我不希望被重定向到其他地方,我只需要在链接被点击后下载文件。这是否是正确的实现方式?我不确定。


<a th:href="${'products/download?id=' + product.id}"><span th:text="@{product.name}"></span></a> 开头的 $ 应该改成 @,你可以试一下这种方式吗? - mtyurt
是的。但错误仍然存在。现在实际ID已经显示在获取请求中了。 - shyam
那个问题的第二个答案就是我所做的,据我所见。而且,在我看来,问题在于当你点击链接时地址栏上的URL会改变。对于Thymeleaf来说,它没有与该URL相关的HTML模板,因此会抛出错误。 - shyam
你是否包含了@ResponseBody注解? - Leandro Carracedo
1
@PatrickLC,这就解决了!如果你把它作为答案发布,我会接受的。 - shyam
显示剩余3条评论
5个回答

14
你需要将 th:href 改为以下格式:
<a th:href="@{|/products/download?id=${product.id}|}"><span th:text="${product.name}"></span></a>

然后你还需要更改你的控制器,并包含@ResponseBody注释:

@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()));
}

嘿,我刚刚注意到你使用了 | 符号而不是通常的 '。有什么需要我知道的吗? - shyam
4
这只是另一种连接字符串的方式,但比使用“.”更容易。请查看此答案:https://dev59.com/hmEh5IYBdhLWcg3wwlu8#22064656 - Leandro Carracedo
太好了!感谢你提供的信息! :) - shyam

5

像这样具有 href url 的链接

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

然后,在控制器中定义如下内容,以将文件写入响应对象。

@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();

        }
    }

1
这对我有效,第一个答案给了我一屏幕的奇怪字符... - Luis Manrique

0

Thymeleaf <td><a th:href="${fileDetail.MsgFileName}" th:text="${fileDetail.MsgFileName}" /></td>。URL 在服务器端准备,href 在客户端创建


0

对我来说,@leandro-carracedo的答案有效,但浏览器只会列出文件内容,而不是下载。

这个修复了这个问题:

<a download="results.csv" th:href=... </a>

0

Thymeleaf非常好文档化,您可以在手册中找到关于字面替换(Leandro上面展示的内容)的信息 - 第4.7节

手册摘录

<span th:text="|Welcome to our application, ${user.name}!|">

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接