I set filename in the HttpServletResponse
header but when I download it has not this filename
FileInputStream fis = new FileInputStream(fileDocument
You should not use the HttpServletResponse
. Instead you should create a custom Resource and ResourceHandler:
CustomResourceHandler.java
:
public final class CustomResourceHandler extends ResourceHandlerWrapper {
// Public Constants
public static final String LIBRARY_NAME = "exampleLib";
public static final String RESOURCE_NAME = "exampleName";
// Private Data Members
private ResourceHandler wrappedResourceHandler;
public CustomResourceHandler(ResourceHandler resourceHandler) {
this.wrappedResourceHandler = resourceHandler;
}
@Override
public Resource createResource(String resourceName, String libraryName) {
if (LIBRARY_NAME.equals(libraryName)) {
if (RESOURCE_NAME.equals(resourceName)) {
return new CustomResource(libraryName, resourceName,
"exampleFileName.txt", "Example Content");
}
else {
return super.createResource(resourceName, libraryName);
}
}
else {
return super.createResource(resourceName, libraryName);
}
}
@Override
public ResourceHandler getWrapped() {
return wrappedResourceHandler;
}
@Override
public boolean libraryExists(String libraryName) {
if (LIBRARY_NAME.equals(libraryName)) {
return true;
}
else {
return super.libraryExists(libraryName);
}
}
/* package-private */ static final class CustomResource extends Resource {
private final String content;
private final Map<String, String> responseHeaders;
private final String requestPath;
private final URL url;
public CustomResource(String libraryName, String resourceName,
String fileName, String content) {
super.setLibraryName(libraryName);
super.setResourceName(resourceName);
super.setContentType("text/plain");
Map<String, String> responseHeaders = new HashMap<String, String>();
responseHeaders.put("Content-Disposition",
"attachment; filename=" + fileName + ";");
this.responseHeaders = Collections.unmodifiableMap(responseHeaders);
StringBuilder sb = new StringBuilder();
sb.append(ResourceHandler.RESOURCE_IDENTIFIER);
sb.append("/");
sb.append(super.getResourceName());
sb.append("?ln=");
sb.append(super.getLibraryName());
this.requestPath = sb.toString();
URL url;
try {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
url = new URL(externalContext.encodeResourceURL(this.requestPath));
}
catch (MalformedURLException e) {
url = null;
}
this.url = url;
this.content = content;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(
content.getBytes(StandardCharsets.UTF_8));
}
@Override
public String getRequestPath() {
return requestPath;
}
@Override
public Map<String, String> getResponseHeaders() {
return responseHeaders;
}
@Override
public URL getURL() {
return url;
}
@Override
public boolean userAgentNeedsUpdate(FacesContext facesContext) {
// Return false if the content cannot change dynamically.
return true;
}
}
}
WEB-INF/faces-config.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<application>
<!-- ... -->
<resource-handler>custom.resource.handler.CustomResourceHandler</resource-handler>
</application>
<!-- ... -->
</faces-config>
Here's some example code for downloading the resource:
<h:outputLink target="_blank" value="#{bean.getDownloadURL(facesContext)}">
<h:outputText value="download" />
</h:outputLink>
public String getDownloadURL(FacesContext facesContext) {
return facesContext.getApplication().getResourceHandler()
.createResource(CustomResourceHandler.RESOURCE_NAME,
CustomResourceHandler.LIBRARY_NAME)
.getURL().toString();
}
You can also look at the Liferay Faces JSF Export PDF for a full portlet example to download/export a file.
If you are in the action or render phase of your portlet (what I would guess for a JSF portlet): there is a ResponseWrapper
which will suppress any header. I'm using this method to find the response for sending binary data during the JSF lifecycle:
public static HttpServletResponse getHttpServletResponse() {
final FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext == null) {
throw new IllegalStateException("Not inside a JSF request");
}
final Object responseObject = facesContext.getExternalContext().getResponse();
if (responseObject instanceof HttpServletResponse) {
return (HttpServletResponse) responseObject;
}
if (responseObject instanceof PortletResponse) {
// Use Liferays util to find the real response
HttpServletResponse response = PortalUtil.getHttpServletResponse((PortletResponse) responseObject);
// Find the outer most response (setting the headers would have no effect, as we are included)
while (response instanceof ServletResponseWrapper) {
final ServletResponse servletResponse = ((ServletResponseWrapper) response).getResponse();
if (!(servletResponse instanceof HttpServletResponse)) {
break;
}
response = (HttpServletResponse) servletResponse;
}
return response;
}
throw new IllegalStateException("Unknown type of response object: " + responseObject);
}