downloading file from Spring Rest Controller - excel

I am trying to download a file through Spring REST Controller. Below is my code -
#RequestMapping(value="/aaa",method=RequestMethod.GET,produces=MediaType.APPLICATION_OCTATE_STREAM_VALUE)
public ResponseEntity<byte[]> testMethod(#RequestParam("test") String test) {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentDispositionFromData("attachment","testExcel.xlsx");
responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
File file = new File("C:\\testExcel.xlsx");
Path path = Paths.get(file.getAbsolutePath());
ByteArrayResource resource = new ByteArrayResource(Files.readAllbytes(path));
return new ResposeEntity<byte[]>(resource.getByteArray(),responseHeaders,HttpStatus.OK);
}
This is called in a button click. After I click the button nothing happens. While debugging this java, I could see the bytestream. In developer tools of Mozilla, I could see successful HTTP response(response in bytestream of that excel file). As per resources available on internet, browser should automatically download the file, but that's not happening.
Why downloading is not happening in browser? What's more I need to do to make it work?
NOTE : This is just for POC purpose. In actual time, I have to generate the excel file from database details, through Apache POI or some other APIs.

The below works for me:
#RequestMapping("/")
public void index(HttpServletRequest request, HttpServletResponse response) throws IOException {
FileInputStream inputStream = new FileInputStream(new File("C:\\work\\boot\\pom.xml"));
response.setHeader("Content-Disposition", "attachment; filename=\"testExcel.xlsx\"");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
ServletOutputStream outputStream = response.getOutputStream();
IOUtils.copy(inputStream, outputStream);
outputStream.close();
inputStream.close();
}

What worked in my case was to make server send the following header:
Access-Control-Expose-Headers: Content-Disposition
So my problem had to do with CSRF issue.

Change the code like this:
public HttpEntity<byte[]> testMethod(#RequestParam("test") String test) {
File file = new File("C:\\testExcel.xlsx");
FileInputStream fileInputStream = new FileInputStream(file);
byte[] array = IOUtils.toByteArray(fileInputStream);
HttpHeaders header = new HttpHeaders();
header.set("Content-type","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
header.set("Content-Disposition", "attachment; filename=" + nameyouwantsaveyourfile + ".xlsx");
header.setContentLength(array.length);
fileInputStream.close();
return new HttpEntity<byte[]>(array, header);
}

Related

How to download excel file using JSF and Java [duplicate]

Is there any way of providing a file download from a JSF backing bean action method?
I have tried a lot of things. Main problem is that I cannot figure how to get the OutputStream of the response in order to write the file content to. I know how to do it with a Servlet, but this cannot be invoked from a JSF form and requires a new request.
How can I get the OutputStream of the response from the current FacesContext?
Introduction
You can get everything through ExternalContext. In JSF 1.x, you can get the raw HttpServletResponse object by ExternalContext#getResponse(). In JSF 2.x, you can use the bunch of new delegate methods like ExternalContext#getResponseOutputStream() without the need to grab the HttpServletResponse from under the JSF hoods.
On the response, you should set the Content-Type header so that the client knows which application to associate with the provided file. And, you should set the Content-Length header so that the client can calculate the download progress, otherwise it will be unknown. And, you should set the Content-Disposition header to attachment if you want a Save As dialog, otherwise the client will attempt to display it inline. Finally just write the file content to the response output stream.
Most important part is to call FacesContext#responseComplete() to inform JSF that it should not perform navigation and rendering after you've written the file to the response, otherwise the end of the response will be polluted with the HTML content of the page, or in older JSF versions, you will get an IllegalStateException with a message like getoutputstream() has already been called for this response when the JSF implementation calls getWriter() to render HTML.
Turn off ajax / don't use remote command!
You only need to make sure that the action method is not called by an ajax request, but that it is called by a normal request as you fire with <h:commandLink> and <h:commandButton>. Ajax requests and remote commands are handled by JavaScript which in turn has, due to security reasons, no facilities to force a Save As dialogue with the content of the ajax response.
In case you're using e.g. PrimeFaces <p:commandXxx>, then you need to make sure that you explicitly turn off ajax via ajax="false" attribute. In case you're using ICEfaces, then you need to nest a <f:ajax disabled="true" /> in the command component.
Generic JSF 2.x example
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
ec.responseReset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
ec.setResponseContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ExternalContext#getMimeType() for auto-detection based on filename.
ec.setResponseContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.
OutputStream output = ec.getResponseOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the usual way.
// ...
fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}
Generic JSF 1.x example
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse();
response.reset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
response.setContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ServletContext#getMimeType() for auto-detection based on filename.
response.setContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.
OutputStream output = response.getOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the usual way.
// ...
fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}
Common static file example
In case you need to stream a static file from the local disk file system, substitute the code as below:
File file = new File("/path/to/file.ext");
String fileName = file.getName();
String contentType = ec.getMimeType(fileName); // JSF 1.x: ((ServletContext) ec.getContext()).getMimeType(fileName);
int contentLength = (int) file.length();
// ...
Files.copy(file.toPath(), output);
Common dynamic file example
In case you need to stream a dynamically generated file, such as PDF or XLS, then simply provide output there where the API being used expects an OutputStream.
E.g. iText PDF:
String fileName = "dynamic.pdf";
String contentType = "application/pdf";
// ...
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, output);
document.open();
// Build PDF content here.
document.close();
E.g. Apache POI HSSF:
String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";
// ...
HSSFWorkbook workbook = new HSSFWorkbook();
// Build XLS content here.
workbook.write(output);
workbook.close();
Note that you cannot set the content length here. So you need to remove the line to set response content length. This is technically no problem, the only disadvantage is that the enduser will be presented an unknown download progress. In case this is important, then you really need to write to a local (temporary) file first and then provide it as shown in previous chapter.
Utility method
If you're using JSF utility library OmniFaces, then you can use one of the three convenient Faces#sendFile() methods taking either a File, or an InputStream, or a byte[], and specifying whether the file should be downloaded as an attachment (true) or inline (false).
public void download() throws IOException {
Faces.sendFile(file, true);
}
Yes, this code is complete as-is. You don't need to invoke responseComplete() and so on yourself. This method also properly deals with IE-specific headers and UTF-8 filenames. You can find source code here.
public void download() throws IOException
{
File file = new File("file.txt");
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response =
(HttpServletResponse) facesContext.getExternalContext().getResponse();
response.reset();
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=file.txt");
OutputStream responseOutputStream = response.getOutputStream();
InputStream fileInputStream = new FileInputStream(file);
byte[] bytesBuffer = new byte[2048];
int bytesRead;
while ((bytesRead = fileInputStream.read(bytesBuffer)) > 0)
{
responseOutputStream.write(bytesBuffer, 0, bytesRead);
}
responseOutputStream.flush();
fileInputStream.close();
responseOutputStream.close();
facesContext.responseComplete();
}
This is what worked for me:
public void downloadFile(String filename) throws IOException {
final FacesContext fc = FacesContext.getCurrentInstance();
final ExternalContext externalContext = fc.getExternalContext();
final File file = new File(filename);
externalContext.responseReset();
externalContext.setResponseContentType(ContentType.APPLICATION_OCTET_STREAM.getMimeType());
externalContext.setResponseContentLength(Long.valueOf(file.lastModified()).intValue());
externalContext.setResponseHeader("Content-Disposition", "attachment;filename=" + file.getName());
final HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
FileInputStream input = new FileInputStream(file);
byte[] buffer = new byte[1024];
final ServletOutputStream out = response.getOutputStream();
while ((input.read(buffer)) != -1) {
out.write(buffer);
}
out.flush();
fc.responseComplete();
}
This is my solution, an extension of BalusC's answer
public static void download(
ByteArrayOutputStream baos,
String downloadFileName,
String contentType
) {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
externalContext.responseReset();
HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
response.reset();
response.setContentType(contentType);
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
Integer size = baos.size();
response.setHeader("Content-Length", size.toString());
response.setHeader(
"Content-Disposition",
"attachment; filename=\"" + downloadFileName + "\""
);
try {
try (OutputStream responseOs = response.getOutputStream()) {
baos.writeTo(responseOs);
}
}
catch (IOException e) {
throw new IOUncheckedException(e);
}
context.responseComplete();
}
here is the complete code snippet http://bharatonjava.wordpress.com/2013/02/01/downloading-file-in-jsf-2/
#ManagedBean(name = "formBean")
#SessionScoped
public class FormBean implements Serializable
{
private static final long serialVersionUID = 1L;
/**
* Download file.
*/
public void downloadFile() throws IOException
{
File file = new File("C:\\docs\\instructions.txt");
InputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int offset = 0;
int numRead = 0;
while ((offset < buf.length) && ((numRead = fis.read(buf, offset, buf.length -offset)) >= 0))
{
offset += numRead;
}
fis.close();
HttpServletResponse response =
(HttpServletResponse) FacesContext.getCurrentInstance()
.getExternalContext().getResponse();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=instructions.txt");
response.getOutputStream().write(buf);
response.getOutputStream().flush();
response.getOutputStream().close();
FacesContext.getCurrentInstance().responseComplete();
}
}
You may change the file reading logic in case you want file to get generated at runtime.

Send excel files from micro to micro

I am trying to establish a communication between two micros, where the first one receives a petition from the frontend and contacts with another micro, which creates an excel file and sends it back to the first micro, starting the download of the file.
First, I tried to print a simple String response to check the communication between micros and that worked correctly, but the same thing for files didn't work.
Here is the code that I am following:
For Controller A:
#GetMapping(path="/test")
public void getEntities2(HttpServletRequest request, HttpServletResponse response) throws IOException {
ResponseEntity<Workbook> responseEntity = new RestTemplate()
.getForEntity("http://localhost:8080/proceso/excel", Workbook.class);
assertThat(responseEntity.getStatusCode(), equalTo(HttpStatus.OK));
response.setHeader("Content-disposition", "attachment; filename=" + "test.xls");
Workbook workbook = responseEntity.getBody();
workbook.write(response.getOutputStream());
workbook.close();
}
The second Controller (Excel file generator):
#GetMapping(path="/excel")
public ResponseEntity<Workbook> test3(HttpServletResponse response) throws Exception {
HashMap<String, ArrayList<String>> test = new HashMap<String, ArrayList<String>>();
Workbook workbook = CreateExcel.excelCreator(test);
Workbook responseFile = workbook;
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + "test.xls")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(responseFile);
}
And then the following error appears:
org.springframework.web.client.HttpClientErrorException$NotAcceptable: 406 null
I just want to receive the file in the first controller and download the Excel from localhost. Is there any way to do it? Thanks all.
I discovered the problem. The excel file is a octet-stream file so it needs to be received as a byte[] array. This is the Controller A:
#GetMapping(path="/")
public ResponseEntity<byte[]> getEntities() throws IOException {
ResponseEntity<byte[]> responseEntity = new RestTemplate()
.getForEntity("http://localhost:8080/excel", byte[].class);
assertThat(responseEntity.getStatusCode(), equalTo(HttpStatus.OK));
return responseEntity;
}
And Controller B:
#GetMapping(path="/excel")
public void getExcel(HttpServletResponse response) throws Exception {
Iterable<InstancesAggregate> list = instancesAggregate.myfindAll(); //Creating a list
HashMap<String, ArrayList<String>> test = CreateExcel.setExcelProceso(list);
Workbook workbook = CreateExcel.excelCreator(test);
response.setHeader("Content-disposition", "attachment; filename=" + "test.xls");
workbook.write(response.getOutputStream());
workbook.close();
}
This works fine for me, hope someone find the info useful.

Download dynamically generated Excel spreadsheet in JSF doesn't work [duplicate]

Is there any way of providing a file download from a JSF backing bean action method?
I have tried a lot of things. Main problem is that I cannot figure how to get the OutputStream of the response in order to write the file content to. I know how to do it with a Servlet, but this cannot be invoked from a JSF form and requires a new request.
How can I get the OutputStream of the response from the current FacesContext?
Introduction
You can get everything through ExternalContext. In JSF 1.x, you can get the raw HttpServletResponse object by ExternalContext#getResponse(). In JSF 2.x, you can use the bunch of new delegate methods like ExternalContext#getResponseOutputStream() without the need to grab the HttpServletResponse from under the JSF hoods.
On the response, you should set the Content-Type header so that the client knows which application to associate with the provided file. And, you should set the Content-Length header so that the client can calculate the download progress, otherwise it will be unknown. And, you should set the Content-Disposition header to attachment if you want a Save As dialog, otherwise the client will attempt to display it inline. Finally just write the file content to the response output stream.
Most important part is to call FacesContext#responseComplete() to inform JSF that it should not perform navigation and rendering after you've written the file to the response, otherwise the end of the response will be polluted with the HTML content of the page, or in older JSF versions, you will get an IllegalStateException with a message like getoutputstream() has already been called for this response when the JSF implementation calls getWriter() to render HTML.
Turn off ajax / don't use remote command!
You only need to make sure that the action method is not called by an ajax request, but that it is called by a normal request as you fire with <h:commandLink> and <h:commandButton>. Ajax requests and remote commands are handled by JavaScript which in turn has, due to security reasons, no facilities to force a Save As dialogue with the content of the ajax response.
In case you're using e.g. PrimeFaces <p:commandXxx>, then you need to make sure that you explicitly turn off ajax via ajax="false" attribute. In case you're using ICEfaces, then you need to nest a <f:ajax disabled="true" /> in the command component.
Generic JSF 2.x example
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
ec.responseReset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
ec.setResponseContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ExternalContext#getMimeType() for auto-detection based on filename.
ec.setResponseContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.
OutputStream output = ec.getResponseOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the usual way.
// ...
fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}
Generic JSF 1.x example
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse();
response.reset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
response.setContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ServletContext#getMimeType() for auto-detection based on filename.
response.setContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.
OutputStream output = response.getOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the usual way.
// ...
fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}
Common static file example
In case you need to stream a static file from the local disk file system, substitute the code as below:
File file = new File("/path/to/file.ext");
String fileName = file.getName();
String contentType = ec.getMimeType(fileName); // JSF 1.x: ((ServletContext) ec.getContext()).getMimeType(fileName);
int contentLength = (int) file.length();
// ...
Files.copy(file.toPath(), output);
Common dynamic file example
In case you need to stream a dynamically generated file, such as PDF or XLS, then simply provide output there where the API being used expects an OutputStream.
E.g. iText PDF:
String fileName = "dynamic.pdf";
String contentType = "application/pdf";
// ...
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, output);
document.open();
// Build PDF content here.
document.close();
E.g. Apache POI HSSF:
String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";
// ...
HSSFWorkbook workbook = new HSSFWorkbook();
// Build XLS content here.
workbook.write(output);
workbook.close();
Note that you cannot set the content length here. So you need to remove the line to set response content length. This is technically no problem, the only disadvantage is that the enduser will be presented an unknown download progress. In case this is important, then you really need to write to a local (temporary) file first and then provide it as shown in previous chapter.
Utility method
If you're using JSF utility library OmniFaces, then you can use one of the three convenient Faces#sendFile() methods taking either a File, or an InputStream, or a byte[], and specifying whether the file should be downloaded as an attachment (true) or inline (false).
public void download() throws IOException {
Faces.sendFile(file, true);
}
Yes, this code is complete as-is. You don't need to invoke responseComplete() and so on yourself. This method also properly deals with IE-specific headers and UTF-8 filenames. You can find source code here.
public void download() throws IOException
{
File file = new File("file.txt");
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response =
(HttpServletResponse) facesContext.getExternalContext().getResponse();
response.reset();
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=file.txt");
OutputStream responseOutputStream = response.getOutputStream();
InputStream fileInputStream = new FileInputStream(file);
byte[] bytesBuffer = new byte[2048];
int bytesRead;
while ((bytesRead = fileInputStream.read(bytesBuffer)) > 0)
{
responseOutputStream.write(bytesBuffer, 0, bytesRead);
}
responseOutputStream.flush();
fileInputStream.close();
responseOutputStream.close();
facesContext.responseComplete();
}
This is what worked for me:
public void downloadFile(String filename) throws IOException {
final FacesContext fc = FacesContext.getCurrentInstance();
final ExternalContext externalContext = fc.getExternalContext();
final File file = new File(filename);
externalContext.responseReset();
externalContext.setResponseContentType(ContentType.APPLICATION_OCTET_STREAM.getMimeType());
externalContext.setResponseContentLength(Long.valueOf(file.lastModified()).intValue());
externalContext.setResponseHeader("Content-Disposition", "attachment;filename=" + file.getName());
final HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
FileInputStream input = new FileInputStream(file);
byte[] buffer = new byte[1024];
final ServletOutputStream out = response.getOutputStream();
while ((input.read(buffer)) != -1) {
out.write(buffer);
}
out.flush();
fc.responseComplete();
}
This is my solution, an extension of BalusC's answer
public static void download(
ByteArrayOutputStream baos,
String downloadFileName,
String contentType
) {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
externalContext.responseReset();
HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
response.reset();
response.setContentType(contentType);
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
Integer size = baos.size();
response.setHeader("Content-Length", size.toString());
response.setHeader(
"Content-Disposition",
"attachment; filename=\"" + downloadFileName + "\""
);
try {
try (OutputStream responseOs = response.getOutputStream()) {
baos.writeTo(responseOs);
}
}
catch (IOException e) {
throw new IOUncheckedException(e);
}
context.responseComplete();
}
here is the complete code snippet http://bharatonjava.wordpress.com/2013/02/01/downloading-file-in-jsf-2/
#ManagedBean(name = "formBean")
#SessionScoped
public class FormBean implements Serializable
{
private static final long serialVersionUID = 1L;
/**
* Download file.
*/
public void downloadFile() throws IOException
{
File file = new File("C:\\docs\\instructions.txt");
InputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int offset = 0;
int numRead = 0;
while ((offset < buf.length) && ((numRead = fis.read(buf, offset, buf.length -offset)) >= 0))
{
offset += numRead;
}
fis.close();
HttpServletResponse response =
(HttpServletResponse) FacesContext.getCurrentInstance()
.getExternalContext().getResponse();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=instructions.txt");
response.getOutputStream().write(buf);
response.getOutputStream().flush();
response.getOutputStream().close();
FacesContext.getCurrentInstance().responseComplete();
}
}
You may change the file reading logic in case you want file to get generated at runtime.

Commandlink won't start downloading a file generated with XDocReport [duplicate]

Is there any way of providing a file download from a JSF backing bean action method?
I have tried a lot of things. Main problem is that I cannot figure how to get the OutputStream of the response in order to write the file content to. I know how to do it with a Servlet, but this cannot be invoked from a JSF form and requires a new request.
How can I get the OutputStream of the response from the current FacesContext?
Introduction
You can get everything through ExternalContext. In JSF 1.x, you can get the raw HttpServletResponse object by ExternalContext#getResponse(). In JSF 2.x, you can use the bunch of new delegate methods like ExternalContext#getResponseOutputStream() without the need to grab the HttpServletResponse from under the JSF hoods.
On the response, you should set the Content-Type header so that the client knows which application to associate with the provided file. And, you should set the Content-Length header so that the client can calculate the download progress, otherwise it will be unknown. And, you should set the Content-Disposition header to attachment if you want a Save As dialog, otherwise the client will attempt to display it inline. Finally just write the file content to the response output stream.
Most important part is to call FacesContext#responseComplete() to inform JSF that it should not perform navigation and rendering after you've written the file to the response, otherwise the end of the response will be polluted with the HTML content of the page, or in older JSF versions, you will get an IllegalStateException with a message like getoutputstream() has already been called for this response when the JSF implementation calls getWriter() to render HTML.
Turn off ajax / don't use remote command!
You only need to make sure that the action method is not called by an ajax request, but that it is called by a normal request as you fire with <h:commandLink> and <h:commandButton>. Ajax requests and remote commands are handled by JavaScript which in turn has, due to security reasons, no facilities to force a Save As dialogue with the content of the ajax response.
In case you're using e.g. PrimeFaces <p:commandXxx>, then you need to make sure that you explicitly turn off ajax via ajax="false" attribute. In case you're using ICEfaces, then you need to nest a <f:ajax disabled="true" /> in the command component.
Generic JSF 2.x example
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
ec.responseReset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
ec.setResponseContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ExternalContext#getMimeType() for auto-detection based on filename.
ec.setResponseContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.
OutputStream output = ec.getResponseOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the usual way.
// ...
fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}
Generic JSF 1.x example
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse();
response.reset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
response.setContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ServletContext#getMimeType() for auto-detection based on filename.
response.setContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.
OutputStream output = response.getOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the usual way.
// ...
fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}
Common static file example
In case you need to stream a static file from the local disk file system, substitute the code as below:
File file = new File("/path/to/file.ext");
String fileName = file.getName();
String contentType = ec.getMimeType(fileName); // JSF 1.x: ((ServletContext) ec.getContext()).getMimeType(fileName);
int contentLength = (int) file.length();
// ...
Files.copy(file.toPath(), output);
Common dynamic file example
In case you need to stream a dynamically generated file, such as PDF or XLS, then simply provide output there where the API being used expects an OutputStream.
E.g. iText PDF:
String fileName = "dynamic.pdf";
String contentType = "application/pdf";
// ...
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, output);
document.open();
// Build PDF content here.
document.close();
E.g. Apache POI HSSF:
String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";
// ...
HSSFWorkbook workbook = new HSSFWorkbook();
// Build XLS content here.
workbook.write(output);
workbook.close();
Note that you cannot set the content length here. So you need to remove the line to set response content length. This is technically no problem, the only disadvantage is that the enduser will be presented an unknown download progress. In case this is important, then you really need to write to a local (temporary) file first and then provide it as shown in previous chapter.
Utility method
If you're using JSF utility library OmniFaces, then you can use one of the three convenient Faces#sendFile() methods taking either a File, or an InputStream, or a byte[], and specifying whether the file should be downloaded as an attachment (true) or inline (false).
public void download() throws IOException {
Faces.sendFile(file, true);
}
Yes, this code is complete as-is. You don't need to invoke responseComplete() and so on yourself. This method also properly deals with IE-specific headers and UTF-8 filenames. You can find source code here.
public void download() throws IOException
{
File file = new File("file.txt");
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response =
(HttpServletResponse) facesContext.getExternalContext().getResponse();
response.reset();
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=file.txt");
OutputStream responseOutputStream = response.getOutputStream();
InputStream fileInputStream = new FileInputStream(file);
byte[] bytesBuffer = new byte[2048];
int bytesRead;
while ((bytesRead = fileInputStream.read(bytesBuffer)) > 0)
{
responseOutputStream.write(bytesBuffer, 0, bytesRead);
}
responseOutputStream.flush();
fileInputStream.close();
responseOutputStream.close();
facesContext.responseComplete();
}
This is what worked for me:
public void downloadFile(String filename) throws IOException {
final FacesContext fc = FacesContext.getCurrentInstance();
final ExternalContext externalContext = fc.getExternalContext();
final File file = new File(filename);
externalContext.responseReset();
externalContext.setResponseContentType(ContentType.APPLICATION_OCTET_STREAM.getMimeType());
externalContext.setResponseContentLength(Long.valueOf(file.lastModified()).intValue());
externalContext.setResponseHeader("Content-Disposition", "attachment;filename=" + file.getName());
final HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
FileInputStream input = new FileInputStream(file);
byte[] buffer = new byte[1024];
final ServletOutputStream out = response.getOutputStream();
while ((input.read(buffer)) != -1) {
out.write(buffer);
}
out.flush();
fc.responseComplete();
}
This is my solution, an extension of BalusC's answer
public static void download(
ByteArrayOutputStream baos,
String downloadFileName,
String contentType
) {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
externalContext.responseReset();
HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
response.reset();
response.setContentType(contentType);
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
Integer size = baos.size();
response.setHeader("Content-Length", size.toString());
response.setHeader(
"Content-Disposition",
"attachment; filename=\"" + downloadFileName + "\""
);
try {
try (OutputStream responseOs = response.getOutputStream()) {
baos.writeTo(responseOs);
}
}
catch (IOException e) {
throw new IOUncheckedException(e);
}
context.responseComplete();
}
here is the complete code snippet http://bharatonjava.wordpress.com/2013/02/01/downloading-file-in-jsf-2/
#ManagedBean(name = "formBean")
#SessionScoped
public class FormBean implements Serializable
{
private static final long serialVersionUID = 1L;
/**
* Download file.
*/
public void downloadFile() throws IOException
{
File file = new File("C:\\docs\\instructions.txt");
InputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int offset = 0;
int numRead = 0;
while ((offset < buf.length) && ((numRead = fis.read(buf, offset, buf.length -offset)) >= 0))
{
offset += numRead;
}
fis.close();
HttpServletResponse response =
(HttpServletResponse) FacesContext.getCurrentInstance()
.getExternalContext().getResponse();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=instructions.txt");
response.getOutputStream().write(buf);
response.getOutputStream().flush();
response.getOutputStream().close();
FacesContext.getCurrentInstance().responseComplete();
}
}
You may change the file reading logic in case you want file to get generated at runtime.

JSF csv download [duplicate]

Is there any way of providing a file download from a JSF backing bean action method?
I have tried a lot of things. Main problem is that I cannot figure how to get the OutputStream of the response in order to write the file content to. I know how to do it with a Servlet, but this cannot be invoked from a JSF form and requires a new request.
How can I get the OutputStream of the response from the current FacesContext?
Introduction
You can get everything through ExternalContext. In JSF 1.x, you can get the raw HttpServletResponse object by ExternalContext#getResponse(). In JSF 2.x, you can use the bunch of new delegate methods like ExternalContext#getResponseOutputStream() without the need to grab the HttpServletResponse from under the JSF hoods.
On the response, you should set the Content-Type header so that the client knows which application to associate with the provided file. And, you should set the Content-Length header so that the client can calculate the download progress, otherwise it will be unknown. And, you should set the Content-Disposition header to attachment if you want a Save As dialog, otherwise the client will attempt to display it inline. Finally just write the file content to the response output stream.
Most important part is to call FacesContext#responseComplete() to inform JSF that it should not perform navigation and rendering after you've written the file to the response, otherwise the end of the response will be polluted with the HTML content of the page, or in older JSF versions, you will get an IllegalStateException with a message like getoutputstream() has already been called for this response when the JSF implementation calls getWriter() to render HTML.
Turn off ajax / don't use remote command!
You only need to make sure that the action method is not called by an ajax request, but that it is called by a normal request as you fire with <h:commandLink> and <h:commandButton>. Ajax requests and remote commands are handled by JavaScript which in turn has, due to security reasons, no facilities to force a Save As dialogue with the content of the ajax response.
In case you're using e.g. PrimeFaces <p:commandXxx>, then you need to make sure that you explicitly turn off ajax via ajax="false" attribute. In case you're using ICEfaces, then you need to nest a <f:ajax disabled="true" /> in the command component.
Generic JSF 2.x example
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
ec.responseReset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
ec.setResponseContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ExternalContext#getMimeType() for auto-detection based on filename.
ec.setResponseContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.
OutputStream output = ec.getResponseOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the usual way.
// ...
fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}
Generic JSF 1.x example
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse();
response.reset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
response.setContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ServletContext#getMimeType() for auto-detection based on filename.
response.setContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.
OutputStream output = response.getOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the usual way.
// ...
fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}
Common static file example
In case you need to stream a static file from the local disk file system, substitute the code as below:
File file = new File("/path/to/file.ext");
String fileName = file.getName();
String contentType = ec.getMimeType(fileName); // JSF 1.x: ((ServletContext) ec.getContext()).getMimeType(fileName);
int contentLength = (int) file.length();
// ...
Files.copy(file.toPath(), output);
Common dynamic file example
In case you need to stream a dynamically generated file, such as PDF or XLS, then simply provide output there where the API being used expects an OutputStream.
E.g. iText PDF:
String fileName = "dynamic.pdf";
String contentType = "application/pdf";
// ...
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, output);
document.open();
// Build PDF content here.
document.close();
E.g. Apache POI HSSF:
String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";
// ...
HSSFWorkbook workbook = new HSSFWorkbook();
// Build XLS content here.
workbook.write(output);
workbook.close();
Note that you cannot set the content length here. So you need to remove the line to set response content length. This is technically no problem, the only disadvantage is that the enduser will be presented an unknown download progress. In case this is important, then you really need to write to a local (temporary) file first and then provide it as shown in previous chapter.
Utility method
If you're using JSF utility library OmniFaces, then you can use one of the three convenient Faces#sendFile() methods taking either a File, or an InputStream, or a byte[], and specifying whether the file should be downloaded as an attachment (true) or inline (false).
public void download() throws IOException {
Faces.sendFile(file, true);
}
Yes, this code is complete as-is. You don't need to invoke responseComplete() and so on yourself. This method also properly deals with IE-specific headers and UTF-8 filenames. You can find source code here.
public void download() throws IOException
{
File file = new File("file.txt");
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response =
(HttpServletResponse) facesContext.getExternalContext().getResponse();
response.reset();
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=file.txt");
OutputStream responseOutputStream = response.getOutputStream();
InputStream fileInputStream = new FileInputStream(file);
byte[] bytesBuffer = new byte[2048];
int bytesRead;
while ((bytesRead = fileInputStream.read(bytesBuffer)) > 0)
{
responseOutputStream.write(bytesBuffer, 0, bytesRead);
}
responseOutputStream.flush();
fileInputStream.close();
responseOutputStream.close();
facesContext.responseComplete();
}
This is what worked for me:
public void downloadFile(String filename) throws IOException {
final FacesContext fc = FacesContext.getCurrentInstance();
final ExternalContext externalContext = fc.getExternalContext();
final File file = new File(filename);
externalContext.responseReset();
externalContext.setResponseContentType(ContentType.APPLICATION_OCTET_STREAM.getMimeType());
externalContext.setResponseContentLength(Long.valueOf(file.lastModified()).intValue());
externalContext.setResponseHeader("Content-Disposition", "attachment;filename=" + file.getName());
final HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
FileInputStream input = new FileInputStream(file);
byte[] buffer = new byte[1024];
final ServletOutputStream out = response.getOutputStream();
while ((input.read(buffer)) != -1) {
out.write(buffer);
}
out.flush();
fc.responseComplete();
}
This is my solution, an extension of BalusC's answer
public static void download(
ByteArrayOutputStream baos,
String downloadFileName,
String contentType
) {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
externalContext.responseReset();
HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
response.reset();
response.setContentType(contentType);
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
Integer size = baos.size();
response.setHeader("Content-Length", size.toString());
response.setHeader(
"Content-Disposition",
"attachment; filename=\"" + downloadFileName + "\""
);
try {
try (OutputStream responseOs = response.getOutputStream()) {
baos.writeTo(responseOs);
}
}
catch (IOException e) {
throw new IOUncheckedException(e);
}
context.responseComplete();
}
here is the complete code snippet http://bharatonjava.wordpress.com/2013/02/01/downloading-file-in-jsf-2/
#ManagedBean(name = "formBean")
#SessionScoped
public class FormBean implements Serializable
{
private static final long serialVersionUID = 1L;
/**
* Download file.
*/
public void downloadFile() throws IOException
{
File file = new File("C:\\docs\\instructions.txt");
InputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int offset = 0;
int numRead = 0;
while ((offset < buf.length) && ((numRead = fis.read(buf, offset, buf.length -offset)) >= 0))
{
offset += numRead;
}
fis.close();
HttpServletResponse response =
(HttpServletResponse) FacesContext.getCurrentInstance()
.getExternalContext().getResponse();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=instructions.txt");
response.getOutputStream().write(buf);
response.getOutputStream().flush();
response.getOutputStream().close();
FacesContext.getCurrentInstance().responseComplete();
}
}
You may change the file reading logic in case you want file to get generated at runtime.

Resources