On button click I would like to call my Service to query database and generate csv file and return in back to user. This process is long running (can take up to 40 sec) I would like not to block user . I would lik to create Progress page, where user can tack a progress of report generation:
My idea is:
Take input from user and submit createReport request to the Service.
Do not wait for the response, return report ID back to user.
The new tab will be open, where user can monitor the status of report generation
User can check the report page to see the status of his request
When request is ready - downloading of report will start in browser.
I would like to have smth like this:
a. Controller method that save the file and return file id.
After that, in this contorller the new thread will call Service and write data to the given file
b. Controller method that show report's status using request params - fileId
Check if file generated or not
If file is generated - write file to the HttpServletResponse response
Question is:
Where should I store file ID and how to connect file ID and file itself? (cache? database?)
How can I check report status? I think about sending AJAX request every 1-2 sec?
Any ideas and recommendations ?
According to me, you can create a servlet to respond with CSV file download. Consider the following process:
On button click open new tab with url (append url with reportId or so)
window.open('/downloadCSV/15646', '_blank');
And then write a servlet for this downloading operation
#RequestMapping(path = "/downloadCSV/{reportId}", method =
RequestMethod.GET)
public void getCSV(#PathVariable String reportId, HttpServletRequest
request, HttpServletResponse response) throws Exception {
byte[] fileContent;
//write your logic or call service to generate csv report and get
bytes of file
response.setContentType("provide content type");
ServletOutputStream out = response.getOutputStream();
out.write(fileContent);
out.flush();
return;
}
this will download the file in opened tab
Related
I want to redirect to a page after writing the excel file. The servlet code is given below:
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
workbook.write(outByteStream);
byte [] outArray = outByteStream.toByteArray();
response.setContentType("application/ms-excel");
response.setContentLength(outArray.length);
response.setHeader("Content-Disposition", "attachment; filename=name_"+date+".xlsx");
response.setIntHeader("Refresh", 1);
OutputStream outStream = response.getOutputStream();
outStream.write(outArray);
response.sendRedirect("url/reports.jsp");
This code downloads an Excel file which i have created.
when i call the above servlet, the excel file is being downloaded but it is throwing following exception in the last line :
Servlet Error: ::java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
Hence i am unable to redirect to a new page. what can i do to access the response object after i write the output in "outStream"
The basic problem is that this ...
I want to redirect to a page after writing the excel file.
... describes two separate responses. The server cannot chain them together by itself because the client will expect only one response to each request. Because two requests are required to elicit two responses, automation of this sequence will require client-side scripting.
Personally, I would probably put the script on the front end: a handler on the appropriate button or link that first downloads the file and then (on success) issues a request for the new page. It would also be possible to do as suggested in comments, however: put script in the new page that downloads the file.
You cannot have a body with a redirect because the browser, when receiving a redirect, will issue a second request to the URL it has found (in header Location), and it's the response of that second request that is displayed, unless it is also a redirect, in which case, it will issue a third request, and so on...
On my company's site we have some tables that we need to export to a csv file.
There are some varying parameters, so the csv file needs to be dynamically created on request.
My problem is that after clicking to download, the response hangs, and waits for the whole file to be created (which can take some time) and only then downloads the entire file in one instant.
I'm using AngularJS, so I'm using window.location = <url_for_file_download> In order to make the browser download the file.
On the server side I'm using Java Spring and I've followed all the instructions I could find on the web in order to create a file download controller.
My controller code is something like this:
#RequestMapping(value = "http://yada.yada.yada/csv/myFile.csv", method = RequestMethod.GET)
public #ResponseBody
void getCustomers(HttpServletResponse response,
#RequestParam(required = false) String someParameters)
throws NotAuthorizedException, IOException {
// set headers
setHeaders(response);
// generate writer
CSVWriter write = generateWriter(response);
// get data
List<String[]> data = getData();
// write and flush and all that
.
.
.
}
My code for setting the response headers are:
response.setContentType("text/csv;charset=utf-8");
response.setHeader("Content-Disposition","attachment; filename=\"" + fileName + ".csv\"");
I've also tried adding the following headers:
response.setHeader("Transfer-Encoding", "Chunked");
response.setHeader("Content-Description", "File Transfer");
and I've also tried setting the Content-type to "application/octet-stream".
Notice that I don't add a Content-length header, since the file doesn't exist yet, and is being written on the fly.
For writing the csv file I'm using OpenCSV and my code is as follows:
OutputStream resOs = response.getOutputStream();
OutputStream buffOs = new BufferedOutputStream(resOs);
OutputStreamWriter outputWriter = new OutputStreamWriter(buffOs,"UTF-8");
CSVWriter writer = new CSVWriter(outputWriter);
I iterate over the data and write it like so:
for (String[] row: data) {
writer.writeNext(line);
}
(It's not exactly the code - but this is more or else what happens in the code)
And at the end I flush and close:
writer.flush();
writer.close();
I also tried flushing after each line I write.
So why isn't the file being transferred before it has all been written?
Why is my browser (Google chrome) downloading the file in one instant after waiting a long time? And how can I fix this.
I hope I've added enough code, if there's something missing just please tell me and I'll try to add it here.
Thank you so much in advance.
Can you try returning a null value in your java
return null ;
Or you can try below code also
1. Jquery code upon clicking the submit button
$(document).ready( function() {
$('#buttonName').click(function(e){
$("#formName").submit();
//alert("The file ready to be downloaded");
});
});
Your controller code
#RequestMapping(value="/name",method=RequestMethod.POST)
public ModelAndView downloadCSV(ModelMap model,HttpSession session,#ModelAttribute(value="Pojo") Pojo pojo
,HttpServletRequest request,HttpServletResponse response){
----------------some code----------------
response.setContentType("application/csv");
("application/unknown");
response.setHeader("content-disposition","attachment;filename =filename.csv");
ServletOutputStream writer = response.getOutputStream();
logger.info("downloading contents to csv");
writer.print("A");
writer.print(',');
writer.println("B");
for(int i=0;i<limit;i++){
writer.print(""+pojo.get(i).getA());
writer.print(',');
writer.print(pojo.get(i).getB());
writer.println();
}
writer.flush();
writer.close();
---------------some code-----------
return null;
}
Hope this helps
The Controller will wait for the response to be written before the response is send back to the client.
Here is a nice post with multiple approaches / options outlined
Downloading a file from spring controllers
This post talks about flushing the output periodically to help fasten the download.
how to download large files without memory issues in java
If all you are trying to do is let the user know that the file download is in progress and due soon, I think an Ajax progress status indicaor might be your solution.
Trigger the ajax call to the back-end to generate the file
Show progress indicator to the user while file is being generated server side
once response is available, file is presented to the user.
I think something similar is being explored here download file with ajax() POST Request via Spring MVC
Hope this helps!
Thanks,
Paul
I faced the same issue. The code that didn't work for me was
#RequestMapping(value = "/test")
public void test(HttpServletResponse response)
throws IOException, InterruptedException {
response.getOutputStream().println("Hello");
response.getOutputStream().flush();
Thread.sleep(2000);
response.getOutputStream().println("How");
response.getOutputStream().flush();
Thread.sleep(2000);
response.getOutputStream().println("are");
response.getOutputStream().flush();
Thread.sleep(2000);
response.getOutputStream().println("you");
response.getOutputStream().flush();
}
The culprit was ShallowEtagHeaderFilter. When this filter is enabled the response is sent in one chunk. When this filter is diabled the response is send in multiple chunks.
From this thread Tomcat does not flush the response buffer it looks like another possible culprit can be GzipFilter
I wrote a simple SpringMVC app and host on a Paas. I have created a table in Mysql and a column is the Blob. I can upload files through the Mysql admin. Right now, my server can serve html file or javascript files correctly in browser. However, when I serve a jpg file in http://myserver.com/File/ad.jpg, my browser showed a small icon and if I save it, the Windows Image software shows that the image is damaged.
Here are some of the code:
#RequestMapping(value="/File/**", //{name:.+}",
method = RequestMethod.GET)
public #ResponseBody void getContent(
// #PathVariable("name") String name,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
String name = request.getPathInfo();
....
IOUtils.copy(blob.getBinaryStream(), out);
I found that getServletContext() returns null, so I wasn't able to get contentType, so I saved contentType in Mysql as image/jpeg for the ad.jpg. I set the disposition to be inline. What else should I do to serve a jpg?
I finally found that the original code has nothing wrong. The original Mysql admin web page uploaded the blob incorrectly. After I found the Paas has a secret new admin page and that can upload a correct blob with an binary option. I still appreciate all the replies in comments.
I know how to download a binary file from my web app by setting the response header and copying the binary file to the response's outputstream. But what I'm having trouble with is returning success so the page will reload. If I return success I will get the error:
java.lang.IllegalStateException: getOutputStream() has already been
called for this response
See the below code example. This will download the file and then throw the exception. Is there a way to restore the response?
public ActionForward export(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
//tell browser program going to return an application file
//instead of html page
response.setContentType("application/force-download");
response.setHeader("Content-Disposition","attachment;filename=temp.csv");
IOUtils.copy(new FileInputStream("/path to some file"), response.getOutputStream());
response.flushBuffer();
return mapping.findForward("success");
}
I don't believe you can do a redirect or reload after a file download. This is more of an HTTP restriction, not something specific to Struts 1.
It takes one HTTP response to download a file to a browser, and one HTTP response to reload the page. You are attempting to do both from the same HTTP request, which simply isn't possible. A request cannot have more than one response.
In much the same way, you can't issue a redirect after you've served a page to the user, unless the page itself contains a <meta refresh="..."> element or some JavaScript that does a reload. Both approaches essentially create another HTTP request, but neither approach is open to you because it's not possible to do either with a file download.
In short, it's not possible to do what you are asking for.
You can set response.setHeader("Refresh", "1"); according to this article:
http://users.polytech.unice.fr/~buffa/cours/internet/POLYS/servlets/Servlet-Tutorial-Response-Headers.html
But it doesn't work when you close browser file download popup.
JSF/Seam. I have a page which accepts some parameters supplied by the user via a form, and then (when the user clicks a button on the page) the server generates a file and sends it in the response such that the user is prompted with a save-as dialog.
Here is the scenario that I'm having trouble with:
If the user initially enters invalid input and then clicks the button, Seam processes the request, but stops at the Process Validations phase. My page then displays the validation error message.
Next, if the user then enters the correct input and clicks the button, Seam calls my action handler, the file is generated and sent to the user in the response - but the validation error message is still displayed!
Initially, I tried some hacks to force the rerendering of the <h:messages/> tag, but nothing was satisfactory. I now suspect the root cause is because Seam does not enter the Render Response phase when I place a file in the response.
Here's my button:
<h:commandButton value="#{messages.Reports_RunReportPDF}"
action="#{bean.generateReportPdf}"/>
And here's my action handler:
public String generateReportPdf() throws IOException {
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)facesContext.getExternalContext().getResponse();
ServletOutputStream servletOutputStream = response.getOutputStream();
// add this header to make browser prompt user with a save-as dialog
response.addHeader("Content-Disposition",
"attachment;filename=" + reportName + ".pdf");
response.setContentType(exportType.contentType());
try {
HashMap<String, Object> parameters = getReportParameters();
ReportContent content = createReport(parameters);
servletOutputStream.write(content.getContents());
servletOutputStream.flush();
servletOutputStream.close();
} catch (ReportingException e) {
e.printStackTrace();
return "fail";
}
return "success";
}
If I comment-out the code which adds the file (and just return "success", the page follows the navigation rules I have set up correctly. But with the file, the page stays exactly the same as it was before the button was pushed.
So, how can I both return a file in the response, and cause the <h:messages/> tag to be rerendered?
So, how can I both return a file in the response, and cause the tag to be rerendered?
That's not possible with a single HTTP request. You can return only one response per request. This is not a JSF limitation, this is a HTTP limitation.
You can use JavaScript to fire two HTTP requests by a single click, but in your particular case this wouldn't work nicely since the JSF request for the message depends on the result of the request for the PDF download. I don't see other ways than letting the servlet set a property of a session scoped managed which represents the status of the PDF export and introduce an ajax poll which requests this property at intervals and stops when it's not null anymore.
Add this to your method after your write
//Skip the rest of JSF phases
FacesContext.getCurrentInstance().responseComplete();
You might also want to add the close in a finally block so that you ensure that the stream is properly closed if exception occurs.
Or add the #Cleanup annotation from Lombok, and it will do it for you automatically
Update
If you are inside a long running conversation, you can end the conversation before the redirect, thus the validation message will be removed.
#End(beforeRedirect=true)