从具有适当扩展名的 JSF 页面下载文件
Download file from JSF page with proper extension
我正在使用此代码从 JSF 页面下载文件。
public String downloadFile(String fileName) {
try {
String reportPath = "/opt/download" + File.separator + selectedDownloadValue + File.separator + fileName;
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
// use this code if the package is located insight the WAR package
File file = new File(reportPath);
if (!file.exists()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "No file " + reportPath);
}
int DEFAULT_BUFFER_SIZE = 10240;
response.setBufferSize(DEFAULT_BUFFER_SIZE);
response.setHeader("Content-Length", String.valueOf(file.length())); // Display file size during download
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Transfer-Encoding", "Binary");
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
BufferedInputStream bIn = new BufferedInputStream(new FileInputStream(file));
int rLength = -1;
byte[] buffer = new byte[1000];
while ((rLength = bIn.read(buffer, 0, 100)) != -1) {
response.getOutputStream().write(buffer, 0, rLength);
}
FacesContext.getCurrentInstance().responseComplete();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
但出于某种原因,我下载的文件的扩展名为 some_file.exe.xht
我尝试添加其他 headers 但没有结果。
也许在下载之前我需要获得文件扩展名?有什么解决办法吗?
为了获得具有正确文件名的下载的最佳跨浏览器兼容性,特别是如果您还想覆盖 Internet Explorer,文件名必须是实际 URL 触发的路径名下载。这不适用于 JSF 表单,因为它们默认提交到 JSF 页面本身的 URL。
实现该目标的最佳方法是创建一个独立的文件 servlet,它侦听前缀 URL 模式,如 /file/*
、/download/*
等,以便文件名可以是作为 URL 的路径名提供。根据您当前的 JSF 方法,下面是独立文件 servlet 的样子,其中有一些改进以减少样板和废话,并修复了 404 行为的错误:
@WebServlet("/download/*")
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fileName = request.getPathInfo().substring(1);
String selectedDownloadValue = request.getParameter("selectedDownloadValue");
String reportPath = "/opt/download/" + selectedDownloadValue + "/" + fileName;
File file = new File(reportPath);
if (!file.exists()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "No file " + reportPath);
return;
}
response.setHeader("Content-Length", String.valueOf(file.length()));
response.setHeader("Content-Type", getServletContext().getMimeType(fileName));
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
Files.copy(file.toPath(), response.getOutputStream());
}
}
如果您已经最低限度地使用 Java 7 和 Java EE 6,则无需进一步 configuration/change。
以上 servlet 由 /download/filename.ext?selectedDownloadValue=foo
提供,将在 另存为 中提供为 filename.ext
所有浏览器,包括顽固地忽略的 Internet Explorer Content-Disposition
header 中的文件名,实际上更喜欢 URL 中的路径名作为默认文件名。
现在,为了从您的 JSF 支持 bean 调用上述 servlet,只需进行重定向。
public void downloadFile(String fileName) throws IOException {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + "/download/" + fileName + "?selectedDownloadValue=" + URLEncoder.encode(selectedDownloadValue, "UTF-8"));
}
如果可能,根据您的业务需求(即不需要 JSF form/conversion/validation/*somemagic*;所有只是 "static"),那么您甚至可以直接 link通过普通 link 或 GET 表单,无需中介 JSF 支持 bean。
<h:outputLink value="#{request.contextPath}/download/#{bean.fileName}">
<f:param name="selectedDownloadValue" value="foo" />
Download
</h:outputLink>
<form action="#{request.contextPath}/download/#{bean.fileName}">
<input type="hidden" name="selectedDownloadValue" value="foo" />
<input type="submit" value="Download" />
</form>
我正在使用此代码从 JSF 页面下载文件。
public String downloadFile(String fileName) {
try {
String reportPath = "/opt/download" + File.separator + selectedDownloadValue + File.separator + fileName;
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
// use this code if the package is located insight the WAR package
File file = new File(reportPath);
if (!file.exists()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "No file " + reportPath);
}
int DEFAULT_BUFFER_SIZE = 10240;
response.setBufferSize(DEFAULT_BUFFER_SIZE);
response.setHeader("Content-Length", String.valueOf(file.length())); // Display file size during download
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Transfer-Encoding", "Binary");
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
BufferedInputStream bIn = new BufferedInputStream(new FileInputStream(file));
int rLength = -1;
byte[] buffer = new byte[1000];
while ((rLength = bIn.read(buffer, 0, 100)) != -1) {
response.getOutputStream().write(buffer, 0, rLength);
}
FacesContext.getCurrentInstance().responseComplete();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
但出于某种原因,我下载的文件的扩展名为 some_file.exe.xht
我尝试添加其他 headers 但没有结果。
也许在下载之前我需要获得文件扩展名?有什么解决办法吗?
为了获得具有正确文件名的下载的最佳跨浏览器兼容性,特别是如果您还想覆盖 Internet Explorer,文件名必须是实际 URL 触发的路径名下载。这不适用于 JSF 表单,因为它们默认提交到 JSF 页面本身的 URL。
实现该目标的最佳方法是创建一个独立的文件 servlet,它侦听前缀 URL 模式,如 /file/*
、/download/*
等,以便文件名可以是作为 URL 的路径名提供。根据您当前的 JSF 方法,下面是独立文件 servlet 的样子,其中有一些改进以减少样板和废话,并修复了 404 行为的错误:
@WebServlet("/download/*")
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fileName = request.getPathInfo().substring(1);
String selectedDownloadValue = request.getParameter("selectedDownloadValue");
String reportPath = "/opt/download/" + selectedDownloadValue + "/" + fileName;
File file = new File(reportPath);
if (!file.exists()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "No file " + reportPath);
return;
}
response.setHeader("Content-Length", String.valueOf(file.length()));
response.setHeader("Content-Type", getServletContext().getMimeType(fileName));
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
Files.copy(file.toPath(), response.getOutputStream());
}
}
如果您已经最低限度地使用 Java 7 和 Java EE 6,则无需进一步 configuration/change。
以上 servlet 由 /download/filename.ext?selectedDownloadValue=foo
提供,将在 另存为 中提供为 filename.ext
所有浏览器,包括顽固地忽略的 Internet Explorer Content-Disposition
header 中的文件名,实际上更喜欢 URL 中的路径名作为默认文件名。
现在,为了从您的 JSF 支持 bean 调用上述 servlet,只需进行重定向。
public void downloadFile(String fileName) throws IOException {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + "/download/" + fileName + "?selectedDownloadValue=" + URLEncoder.encode(selectedDownloadValue, "UTF-8"));
}
如果可能,根据您的业务需求(即不需要 JSF form/conversion/validation/*somemagic*;所有只是 "static"),那么您甚至可以直接 link通过普通 link 或 GET 表单,无需中介 JSF 支持 bean。
<h:outputLink value="#{request.contextPath}/download/#{bean.fileName}">
<f:param name="selectedDownloadValue" value="foo" />
Download
</h:outputLink>
<form action="#{request.contextPath}/download/#{bean.fileName}">
<input type="hidden" name="selectedDownloadValue" value="foo" />
<input type="submit" value="Download" />
</form>