Download a file with JSF? [duplicate] - java

This question already has answers here:
How to provide a file download from a JSF backing bean?
(5 answers)
Closed 7 years ago.
which is the right way to download a file using JSF?, just putting a link to the file ?? in that case how do i get the file URL??
i have seen one example using BufferedInputStream:
http://www.winstonprakash.com/articles/jsf/file_download_link.htm
What are the differences?
Thanks

If it's a simple file, just place in public webcontent (there where you put your static and JSF files) and create a link.
<h:outputLink value="/files/file.ext">link</h:outputLink>
The servletcontainer will worry about applying the correct headers.
If it's located outside the public webcontent for some specific reasons (e.g. in a fixed path at server machine, or in a database), then create a servlet which gets an InputStream of it and writes it to the OutputStream of the response along at least the Content-Type, Content-Disposition and Content-Length headers. You can find here a simple kickoff example. Also that can simply be linked on the servlet's url-pattern.
If it's to be dynamically generated and depending on the JSF specific request parameters, then you can also do so in a managed bean action which is bound by h:commandLink or h:commandButton, but you only need to ensure that you call FacesContext#responseComplete() at end of bean's action method to prevent JSF from taking the navigation in hands. The same kind of servlet code can be reused to stream the file. You can find a kickoff example in this answer.

I needed to make a similar code to download a file via JSF
That's my download button in my JSF page
<h:commandButton value="Download" action="#{helloBean.downloadFile}" />
And it's my Java Code
public void downloadFile() {
File file = new File("/home/marco/file.txt");
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
response.setHeader("Content-Disposition", "attachment;filename=file.txt");
response.setContentLength((int) file.length());
ServletOutputStream out = null;
try {
FileInputStream input = new FileInputStream(file);
byte[] buffer = new byte[1024];
out = response.getOutputStream();
int i = 0;
while ((i = input.read(buffer)) != -1) {
out.write(buffer);
out.flush();
}
FacesContext.getCurrentInstance().getResponseComplete();
} catch (IOException err) {
err.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException err) {
err.printStackTrace();
}
}
}

I have had an error on
FacesContext.getCurrentInstance().getResponseComplete();
from the type
java.lang.IllegalStateException: getOutputStream() has already been called for this response
and i solved it:
JSF page:
<h:commandButton action="#{bean.downloadFile}" id="downloadBtn" value="Download"/>
Bean method:
public void downloadFile(File file) {
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
response.setHeader("Content-Disposition", "attachment;filename=file.txt");
response.setContentLength((int) file.length());
FileInputStream input= null;
try {
int i= 0;
input = new FileInputStream(file);
byte[] buffer = new byte[1024];
while ((i = input.read(buffer)) != -1) {
response.getOutputStream().write(buffer);
response.getOutputStream().flush();
}
facesContext.responseComplete();
facesContext.renderResponse();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(input != null) {
input.close();
}
} catch(IOException e) {
e.printStackTrace();
}
}
}

Related

How to upload zip file through REST API Mule 4?

I'm trying to upload zip file to the url https://anypoint.mulesoft.com/designcenter/api-designer/projects/{projectId}/branches/master/import. Content-Type must be application/zip, cant change to multipart/form-data. In Mule 3, a java transform class is used (com.test.FileReader) with the FileReader.class is stored in lib. It worked in Mule 3.
I tried to use ReadFile component to read test.zip and set as payload but it's not working. Any suggestion how to upload zip file in Mule 4?
package com.test;
import org.mule.transformer.*;
import org.mule.api.*;
import org.mule.api.transformer.*;
import java.io.*;
public class PayloadFileReader extends AbstractMessageTransformer
{
public Object transformMessage(final MuleMessage message, final String outputEncoding) throws TransformerException {
byte[] result = null;
try {
result = this.readZipFile("test.zip");
}
catch (Exception e) {
e.printStackTrace();
}
message.setPayload((Object)result);
return message;
}
public String readFileTest(final String path) throws FileNotFoundException, IOException, Exception {
final ClassLoader classLoader = this.getClass().getClassLoader();
final File file = new File(classLoader.getResource(path).getFile());
final FileReader fileReader = new FileReader(file);
BufferedReader bufferReader = null;
final StringBuilder stringBuffer = new StringBuilder();
try {
bufferReader = new BufferedReader(fileReader);
String line;
while ((line = bufferReader.readLine()) != null) {
stringBuffer.append(line);
}
}
catch (IOException e) {
e.printStackTrace();
if (bufferReader != null) {
try {
bufferReader.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
finally {
if (bufferReader != null) {
try {
bufferReader.close();
}
catch (IOException e2) {
e2.printStackTrace();
}
}
}
return stringBuffer.toString();
}
public byte[] readZipFile(final String path) {
final ClassLoader classLoader = this.getClass().getClassLoader();
final File file = new File(classLoader.getResource(path).getFile());
final byte[] b = new byte[(int)file.length()];
try {
final FileInputStream fileInputStream = new FileInputStream(file);
fileInputStream.read(b);
fileInputStream.close();
}
catch (FileNotFoundException e) {
System.out.println("Not Found.");
e.printStackTrace();
}
catch (IOException e2) {
System.out.println("Error");
e2.printStackTrace();
}
return b;
}
}
'
Assuming that your zip file corresponds to a valid API spec, in Mule 4, you don't need to use a custom java code to achieve what you want: you can read the file content using the File connector Read operation, and use an HTTP Request to upload it to Design Center using Design Center API. Your flow should look like:
For the Read operation, you only need to set the file location, in the File Path operation property.
No need to set content type in the HTTP Request (Mule 4 will configure the content type automatically based on the file content loaded by the Read operation).
You can't use Java code that depends on Mule 3 classes in Mule 4. Don't bother trying to adapt the code, it is not meant to work. Their architecture are just different.
While in Mule 4 you can use plain Java code or create a module with the SDK, there is no reason to do so for this problem and it would be counterproductive. My advise it to forget the Java code and resolve the problem with pure Mule 4 components.
In this case there doesn't seem a need to actually use Java code. The File connector read operation should read the file just fine as it doesn't appear the Java code is doing anything else than reading the file into the payload.
Sending through the HTTP Request connector should be straightforward. You didn't provide any details of the error, (where is it happening, complete error message, HTTP status error code, complete flow with the HTTP request in both versions, etc) and the API Designer REST API doesn't document an import endpoint so it is difficult to say if the request is correctly constructed.

Apache POI 3.14 Writing SXSSF to ServletResponse OutputStream is corrupting workbook JSF

In my webapp, I'm building SXSSFWorkbook objects to generate reports with roughly 30-60k records each. I kick off a process through a request to first fetch and build each SXSSFWorkbook. I'm able to generate the report and open a FileOutputStream to export my object to my desktop(locally of course). However, I want to let user choose which report to download through a request(JSF)to the server. When I feed the OutputStream from the servlet response, I can download the .xlsx file but it tells me it's been corrupted. I've done some research, tried some different workarounds all with no results. Posted is my code. I'm kind of at a loss for what's going on here.
p.s. I was previously generating HSSFWorkbook objects and downloading them but they were starting to causing heap space issues. Hence, the switch to SXSSFWorkbook.
My command button
<h:commandButton value="Download Report" styleClass="secondary-button"
action="#{backingBean.getLatestReport}"
id="thirdReportButton">
</h:commandButton>
My action
public void getLatestReport() throws Exception {
FacesContext faces = FacesContext.getCurrentInstance();
String templateName = "Report.xlsx";
HttpServletResponse response = null;
OutputStream outputStream = null;
//workbookForLatestReport is a SXSSFWorkbook object
try {
if (workbookForLatestReport != null) {
response = (HttpServletResponse) faces.getExternalContext()
.getResponse();
response.reset();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition",
"attachment; filename=\"" + templateName + "\"");
outputStream = response.getOutputStream();
workbookForLatestReport.write(outputStream);
outputStream.close();
workbookForLatestReport.dispose();
}
faces.renderResponse();
} catch (Exception e) {
throw e;
}
}
Recently I successfully accomplished similar task so I might be able to help you.
I've just ran your code (on Payara 4.1 using Chrome as browser) adding part that you omitted in your post
#ManagedBean(name = "backingBean")
#ViewScoped
public class BackingBean {
//your command button should call getLatestReport and not getSecondReport() as in your original post
public void getLatestReport() throws Exception {
FacesContext faces = FacesContext.getCurrentInstance();
String templateName = "Report.xlsx";
HttpServletResponse response = null;
OutputStream outputStream = null;
//workbookForLatestReport is a SXSSFWorkbook object
//MY ADDITION START
//I've created SXSSFWorkbook object since your post did't contain this part
//100K rows, 100 columns
SXSSFWorkbook workbookForLatestReport = new SXSSFWorkbook(SXSSFWorkbook.DEFAULT_WINDOW_SIZE);
workbookForLatestReport.setCompressTempFiles(true);
SXSSFSheet sheet = workbookForLatestReport.createSheet();
for (int rowNumber = 0; rowNumber < 100000; rowNumber++) {
SXSSFRow row = sheet.createRow(rowNumber);
for (int columnNumber = 0; columnNumber < 100; columnNumber++) {
SXSSFCell cell = row.createCell(columnNumber);
cell.setCellValue("ROW " + rowNumber + " COLUMN " + columnNumber);
}
}
//MY ADDITION END
try {
if (workbookForLatestReport != null) {
response = (HttpServletResponse) faces.getExternalContext()
.getResponse();
response.reset();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition",
"attachment; filename=\"" + templateName + "\"");
outputStream = response.getOutputStream();
workbookForLatestReport.write(outputStream);
outputStream.close();
workbookForLatestReport.dispose();
}
faces.renderResponse();
} catch (Exception e) {
throw e;
}
}
}
It was working just fine and as expected.
I have 2 suggestions:
your command button "calls" action="#{backingBean.getSecondReport}"
but your managed bean action is named public void getLatestReport.
Check out if it is typing error or not.
compare your SXSSFWorkbook object creation code with my example. Are
there any crucial differences?
I was able to come to a solution with the aid of omifaces and changing some code around.
After I create my workbook, I use a ByteArrayStream to set a byte[] attribute on my model bean that can be referenced when the download listener from the jsf is clicked.
ByteArrayOutputStream bos;
byte[] workbookForLatestBytes;
XSSFXWorkbook workbookForLatestReport;
.
.
.
workbookForLatestReport = <generate the report here>
if(workbookForLatestReport != null){
bos = new ByteArrayOutputStream();
workbookForLatestReport.write(bos);
workbookForLatestBytes = bos.toByteArray();
workbookForPreviousReport.dispose();
}
Here is the action being fired from my JSF code.
<h:commandButton value="Download Report"
styleClass="secondary-button"
action="#{productRuleAuditCompareBackingBean.getSecondReport}"
id="thirdReportButton"
rendered="#{not empty productRuleAuditCompareModelBean.workbookForLatestBytes}">
</h:commandButton>
My backing bean action is as follows. I reset the HTTP Response before writing the byte array to the response output stream.
public void getSecondReport() {
FacesContext faces = FacesContext.getCurrentInstance();
try {
faces.getExternalContext().responseReset();
this.prAuditCompareModelBean.getLatestReport();
faces.responseComplete();
} catch (Exception e) {
PODBException.wrap(e, PODBExceptionInformation.create(
PODBExceptionType.ERROR,
ProductRuleAuditCompareBackingBean.class.getName(),
"getting first report",
"Error while getting second report for download", "1"));
}
}
Here I'm using Omniface Faces#sendFile method to write the workbook to the response outputstream.
public void getLatestReport() throws Exception {
try {
if (getWorkbookForLatestBytes() != null) {
Faces.sendFile(getWorkbookForLatestBytes(), reportName, true);
}
} catch (Exception e) {
throw e;
}
}

Spring MVC Safe way to Upload, generate and download a file

I´m working on a WebApp with Spring MVC and Maven. I have the following process: First of all the User has to upload a file. Afterwards the uploaded file will be edited. Last but not least I want to create a download which contains the edited file.
The first step "Upload File" works well. I have a controller which contains the following POST method:
#RequestMapping(value = "/CircleUp", method = RequestMethod.POST)
public String circleUpPost(HttpServletRequest request, Model model, //
#ModelAttribute("circleUpForm") CircleUpForm circleUpForm) {
return this.doUpload(request, model, circleUpForm);
}
private String doUpload(HttpServletRequest request, Model model, //
CircleUpForm circleUpForm) {
File file = circleUpForm.getFile();
if (file != null) {
try {
//Some edit stuff
serialize(file, SerializationModeEnum.Standard);
} catch (Exception e) {
e.printStackTrace();
}
}
model.addAttribute("uploadedFiles", file);
return "uploadResult";
}
protected static String serialize(File file, SerializationModeEnum serializationMode) {
java.io.File test = null;
try {
test = java.io.File.createTempFile("Test", ".pdf");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
file.save(test, serializationMode);
} catch (Exception e) {
e.printStackTrace();
}
// test.deleteOnExit();
return test.getPath();
}
In the "serialize" Method my PDFClown File will be saved to a temp folder.
Afterwards the "uploadResult" page will be appear which contains the folloing code:
<%#taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<meta charset="UTF-8">
<title>Download</title>
</head>
<body>
<h3>Download Files:</h3>
CircleUp
</body>
</html>
When the User clicks on the link another Controller will be called which handles the download. I dont know how to design the controller so that it can works with the edited file which I saved in my temp folder. I think it should look like that :
#RequestMapping(value = "/Download")
public void download(HttpServletRequest request, HttpServletResponse response) throws IOException {
final String temperotyFilePath = ???
String fileName = "Test.pdf";
response.setContentType("application/pdf");
response.setHeader("Content-disposition", "attachment; filename=" + fileName);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos = convertPDFToByteArrayOutputStream(temperotyFilePath + "\\" + fileName);
OutputStream os = response.getOutputStream();
baos.writeTo(os);
os.flush();
} catch (Exception e1) {
e1.printStackTrace();
}
}
private ByteArrayOutputStream convertPDFToByteArrayOutputStream(String fileName) {
InputStream inputStream = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
inputStream = new FileInputStream(fileName);
byte [] buffer = new byte [1024];
baos = new ByteArrayOutputStream();
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return baos;
}
I have two questions now:
How can the DownloadController attain the temp path to the file?
Is this process of Uploading,Generating and Downloading a File safe? Or is there a better way to handle this process?
I´m new to Spring MVC and WebApp Development and I´m thankful for every suggestion :)
You can use the same approach you use in the upload
test = java.io.File.createTempFile("Test", ".pdf");
All you need is to point to the same file and then read it.
If you need a custom dir for the files saving you can either define a property - my.file.path=some path here or
use system temp dir
public class Main {
public static void main(String[] args) {
String property = "java.io.tmpdir";
String tempDir = System.getProperty(property);
System.out.println("OS current temporary directory is " + tempDir);
}
}
Got the code from the link
Actually the approach is not safe. What to do if 2 different users upload files with the same name& What if one is uploaded and another user tries to download it? What is amount of files is millions? etc. etc.
It's better to use independent file storage but for test project it's fine

Serlvet mixing session responses

I'm developing an Android printing application. A part of my application is a local server that receive files from the users.
I implemented the server in Tomcat Java servlet.
My problem is that when two devices sending 2 files instantaneously to the server, there is two possible results:
1. One client receives a good response, and the second client receives an empty response.
2. One client receives a response of the second client and vise versa.
Here is my servlet code:
protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
new Runnable() {
#Override
public void run() {
try {
request.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
response.setCharacterEncoding("UTF-8");
try {
writer = response.getWriter();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
// get access to file that is uploaded from client
Part p1 = request.getPart("File");
InputStream is = p1.getInputStream();
// read filename which is sent as a part
Part p2 = request.getPart("MetaData");
Scanner s = new Scanner(p2.getInputStream());
String stringJson = s.nextLine(); // read filename from stream
s.close();
json = new JSONObject(stringJson);
fileName = new String(json.getString("FileName").getBytes("UTF-8"));
fileDirectory = BASE + request.getSession().getId();
File dir = new File(fileDirectory);
dir.mkdir();
// get filename to use on the server
String outputfile = BASE + dir.getName() + "/" + fileName; // get path on the server
FileOutputStream os = new FileOutputStream (outputfile);
// write bytes taken from uploaded file to target file
byte[] buffer = new byte[1024];
int ch = is.read(buffer);
while (ch != -1) {
os.write(buffer);
ch = is.read(buffer);
}
os.close();
is.close();
}
catch(Exception ex) {
writer.println("Exception -->" + ex.getMessage());
}
finally {
try {
myRequest = request;
try {
printFile(request.getSession().getId());
} catch (IOException e) {
// TODO Auto-generated catch block
writer.println("Exception -->" + e.getMessage());
}
writer.close();
} catch (InterruptedException e) {
writer.println("Exception -->" + e.getMessage());
}
}
}
}.run();
}
The tomcat server is running over Ubuntu 13.04 as a virtual machine.
Any idea?
I don't think the use of Runnable makes a difference but it's kind of pointless. I can't see where you declared your writer. If that's an instance variable of the servlet (i.e. not in the post method) then that one is prone to session swapping when used by two sessions at the same time. Try declaring it within the post method.
You need not to implement Runnable. By default Servlets are thread safe, which means a new thread is created for each of the request. If you are using some static variables then make sure you use them in a thread safe way. I think your thread creation may be troubling the tomcat/container to send the response in an incorrect way. In short I believe you are doing more than is required, container is there for your rescue.
Any web container manages multi-threading of servlets. You need not implement your own thread. Remove the multi-threading from your code and it will work perfectly.

java.io.FileNotFoundException when uploading a file from a JSP

I have coded a AJAX file upload feature in my application. It works perfectly when running it from my laptop. When I try the exact same file using the same app, but deployed on a jBoss server, I get the following exception:
2013-02-18 11:30:02,796 ERROR [STDERR] java.io.FileNotFoundException: C:\Users\MyUser\Desktop\TestFile.pdf (The system cannot find the file specified).
getFileData method:
private byte[] getFileData(File file) {
FileInputStream fileInputStream = null;
byte[] bytFileData = null;
try {
fileInputStream = new FileInputStream(file);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
if (fileInputStream != null) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytBuffer = new byte[1024];
try {
for (int readNum; (readNum = fileInputStream.read(bytBuffer)) != -1;) {
byteArrayOutputStream.write(bytBuffer, 0, readNum);
}
} catch (IOException e) {
e.printStackTrace();
}
bytFileData = byteArrayOutputStream.toByteArray();
}
return bytFileData;
}
Getting the file content in a variable (from the method above):
byte[] bytFileData = this.getFileData(file);
Making the file:
private boolean makeFile(File folderToMake, File fileToMake, byte[] bytFileData) {
Boolean booSuccess = false;
FileOutputStream fileOutputStream = null;
try {
if (!folderToMake.exists()) {
folderToMake.mkdirs();
}
if (!fileToMake.exists()) {
if (fileToMake.createNewFile() == true) {
booSuccess = true;
fileOutputStream = new FileOutputStream(fileToMake);
fileOutputStream.write(bytFileData);
fileOutputStream.flush();
fileOutputStream.close();
}
}
} catch (Exception e) {
e.printStackTrace();
booSuccess = false;
}
return booSuccess;
}
Any idea?
Thank you
Charles
It seems you're just passing the file path as part of the request to the server, not actually uploading the file, then attempting to use that file path to access the file.
That will work on your laptop because the code, when running locally, has access to your file system and will be able to locate the file. It won't work deployed on a server because it's an entirely separate machine, and as a result won't have access to your file system.
You'll need to modify your client-side (AJAX) code to actually upload the file, then modify your server-side code to use that uploaded file. Note that AJAX file uploads aren't generally possible - there are plugins for frameworks such as jQuery that provide this functionality using workarounds.
I'm not 100%, but I think proper AJAX file uploads may be possible using HTML5 features, but browser support for that is likely going to be pretty poor right now.

Categories