I'm trying to get my JSF to export a spreadsheet for download. I'm using Apache's POI library for the Excel document writing. I get the following error in an alert box when the code runs:
emptyResponse: An empty response was received from the server.
The method generating the spreadsheet and exporting to the OutputStream is below (I have renamed classes, methods etc for simplicity sake).
private void generateSpreadsheet(Object object) throws Exception {
FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
String fileName = object.getProperty() + ".xlsx";
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName +"\"");
OutputStream os = response.getOutputStream();
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("Sheet 1");
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellValue("test");
wb.write(os);
os.flush();
os.close();
FacesContext.getCurrentInstance().responseComplete();
}
Any advice much appreciated, thanks.
If it makes a difference, I'm using AJAX (<f:ajax> tag) on the form submit that calls this method.
It makes definitely difference. You can not download files by Ajax. Ajax is executed by JavaScript code. But JavaScript has no facilities to force a Save As dialogue or to execute the platform default application associated with the file's mime type (thankfully; that would have been a major security/intrusion problem). Further, the JSF Ajax API expects a XML response in a specified structure conform the JSF specs. When you send a complete Excel file instead, the whole Ajax response would be ignored as ununderstandable garbage by the JSF Ajax API.
You need to send a normal synchronous request. Remove the <f:ajax> tag from the command link/button. The current page will remain the same anyway if the download is sent as an attachment.
Related
When I use #using (Ajax.BeginForm("Generate", "Report")) and press submit button, then the byte file is opened in end of my page
When I use Export
Export2
then the file is downloading and saving in default folder DOWNLOADS. So what is the difference between submit and a href???
public FileContentResult Generate()
{
byte[] resultee2 = new byte[12];
return new FileContentResult(resultee2, "text/plain") { FileDownloadName = "myfile.txt" };
}
And how can I create downloading file to folder after submitting form?
I have a form with fields, then I press submit button and values from fields are being send to server, server is generating byte array and then server is responding that byte array then controller has this byte array and then I want to convert this byte array to text and send to client as Download File.
Form file downloads over AJAX do not always function like opening a file from a hyperlink click or after a standard form post action. What I've done in the past is define an action:
public ActionResult Download(long id)
{
//get record info
return File(fileStream, "application/pdf");
}
I was working with PDFS in this example (note the browser by default, would open in a new tab and not prompt to download directly). And access this action via javascript:
window.open("Download?id=" + id, "_blank");
This will call the download option and return the contents. Note that this requires using some persistence mechanism, like storing the file on disk or in the database, or recreating the file from data in the database.
You also have more control if you use jQuery's $.ajax method because you have more control over the request and response, and could possibly return file data back through the AJAX call response...
After saving data in WFFM Custom save Action,I want to redirect Success Page with some large amount of data
I am trying below line of code .
I Can use Cookies ,session or Query String and Response.Redirect(baseUrl)but i want to Cookies ,session or Query String .
class SaveAction : WffmSaveAction
{
public override void Execute(ID formId, AdaptedResultList adaptedFields, ActionCallContext actionCallContext, params object[] data)
{
//Save Data in Service ,, Redirect to success page with below code with some data like ID
string baseUrl = HttpContext.Current.Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority +
HttpContext.Current.Request.ApplicationPath.TrimEnd('/') + "/success-page";
HttpContext.Current.Response.Clear(); //
StringBuilder sb = new StringBuilder();
sb.Append("<html>");
sb.AppendFormat(#"<body onload='document.forms[""form""].submit()'>");
sb.AppendFormat("<form name='form' action='{0}' method='post'>", baseUrl);
sb.AppendFormat("<input type='hidden' name='id' value='{0}'>", "123456");
// Other params go here
sb.Append("</form>");
sb.Append("</body>");
sb.Append("</html>");
HttpContext.Current.Response.Write(sb.ToString());
HttpContext.Current.Response.End();
// HttpContext.Current.Response.Redirect(baseUrl);
}
}
Above code reload the same page with no body in Html.
Am i missing something in given code ?
The answer might depend on whether you have an mvc form or not.
In case of mvc forms, you might want to read this: http://ggullentops.blogspot.be/2016/07/sitecore-wffm-act-on-success.html. It describes hooking into the success pipeline <wffm.success> and passing data towards the success page (how exactly - querystring, session, .. is up to you(r code)). Fairly easy once you know the correct pipeline.
There is also a great post here describing what you are trying to do - i.e. saving the data for later use in the saveaction. It's too much code to copy here but it comes down to saving the data (in session) during the save action and creating a rendering (to read and handle the data again) that you will place on the success page.
Creating a rendering to place on your success page is something you will have to do anyway.. Don't try to redirect yourself, Sitecore does that for you.
In an ASP.NET MVC web application I write directly to HttpContext.Current.Response to export to a CSV file.
This is done in an action in my controller. So I do something like this:
try
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.Cookies.Clear();
HttpContext.Current.Response.Charset = System.Text.UTF8Encoding.UTF8.WebName;
HttpContext.Current.Response.ContentEncoding = System.Text.UTF8Encoding.UTF8;
HttpContext.Current.Response.AppendHeader("Pragma", "no-cache");
HttpContext.Current.Response.AppendHeader("Content-Disposition", fileName);
HttpContext.Current.Response.ContentType = "text/csv";
HttpContext.Current.Response.Write("a,b,c\n");
}
catch(Exception)
{
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.SuppressContent = true;
return RedirectToAction("Error");
//return PartialView("ErrorExportingData"); //Prefered!
}
This works without any problem, except that the page is not redirected (or the partial view is not displayed). I guess the problem is that a complete response was already created (and completed).
I flush the response and suppress the content before the redirect, so the exception stack trace does not end up in my CSV file. After this I somehow need to build a new response.
My question is: In this situation, How can I redirect to an error page, after an exception was thrown?
(If somebody wonders why I want to write directly to HttpContext.Current.Response? This is because it is the fastest way to write many records to a CSV file, using a SqlDataReader.)
I am using Primefaces DialogFramework with
Primefaces 5.0
Mojarra 2.1.27
Glassfish 3.1.2.2 Build 5
My problem is, that if the user knows the location of my dialog, he is able to access it directly via the URL. I do not want that to be possible, so I thought it would be able to put the dialog in WEB-INF folder of my web-app, but now, if I want to open the dialog, I get a FileNotFound-Exception.
If my dialog is located in some regular folder, it works fine
RequestContext.getCurrentInstance().openDialog("/myfolder/mydialog");
// this works as expected
but if it is located in WEB-INF, it does not work any longer
RequestContext.getCurrentInstance().openDialog("/WEB-INF/mydialog",options,null);
// this is causing a fileNotFoundException
I also tried to set up a navigation rule for this in faces-config but again with no success
<navigation-case>
<from-outcome>mydialog</from-outcome>
<to-view-id>/WEB-INF/mydialog.xhtml</to-view-id>
<redirect />
</navigation-case>
How may I open dialogs located in WEB-INF folder, or is it not possible at all?
Thanks in advance
Unfortunately, putting PrimeFaces Dialog Framework dialogs in /WEB-INF in order to prevent direct access is indeed not going to work. The dialogs are loaded entirely client side. On the POST request which opens the dialog, JSF/PrimeFaces returns an oncomplete script with the (public!) URL of the dialog to JavaScript/jQuery, which in turn shows a basic dialog template with an <iframe> whose URL is set to the dialog URL, which in turn loads the content. In effects, 2 requests are being sent, the first to get the dialog's URL and the second to get the dialog's content based on that URL in the <iframe>.
There's no way to keep the dialog in /WEB-INF without falling back to the "traditional" dialog approach via <p:dialog> and conditional display via JS/CSS. There's also no way in the server side to verify based on some headers if the request is coming from an <iframe>, so that all others could simply be blocked. Your closest bet is the referer header, but this can be spoofed.
One way to minimize abuse is checking the presence of pfdlgcid request parameter (identified by Constants.DIALOG_FRAMEWORK.CONVERSATION_PARAM) when a dialog is being requested. PrimeFaces namely appends this request parameter representing "conversation ID" to the dialog URL. Presuming that all dialogs are stored in a folder /dialogs, then you could do the job with a simple servlet filter. Here's a kickoff example which sends a HTTP 400 error when /dialogs/* is being requested without the pfdlgcid request parameter.
#WebFilter("/dialogs/*")
public class DialogFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String id = request.getParameter(Constants.DIALOG_FRAMEWORK.CONVERSATION_PARAM);
if (id != null) {
chain.doFilter(req, res); // Okay, just continue request.
}
else {
response.sendError(HttpServletResponse.SC_BAD_REQUEST); // 400 error.
}
}
// ...
}
However, the abuser might not be that stupid and discover the pfdlgcid request parameter during the normal flow and still be able to open the dialog individually when supplying that parameter, even with a random value. I thought of comparing the actual pfdlgcid value to the known ones. I checked the PrimeFaces DialogNavigationHandler source code, but unfortunately, PrimeFaces doesn't store this value anywhere in the session. You'd need to provide a custom DialogNavigationHandler implementation wherein you store the pfdlgcid value in the session map which in turn is also compared in the servlet filter.
First add the following method to the DialogFilter:
public static Set<String> getIds(HttpServletRequest request) {
HttpSession session = request.getSession();
Set<String> ids = (Set<String>) session.getAttribute(getClass().getName());
if (ids == null) {
ids = new HashSet<>();
session.setAttribute(getClass().getName(), ids);
}
return ids;
}
Then copypaste the PrimeFaces DialogNavigationHandler source code into your own package and add the following line after line 62:
DialogFilter.getIds((HttpServletRequest) context.getExternalContext().getRequest()).add(pfdlgcid);
Replace the <navigation-handler> in faces-config.xml with the customized one.
Finally, alter the if condition in the DialogFilter#doFilter() method as follows:
if (getIds(request).contains(id)) {
// ...
}
Now, this prevents the abuser from attempting to open the dialog with a random ID. This however doesn't prevent the abuser from attempting to open the dialog by copypasting the exact <iframe> URL immediately after opening it. Given the way how the PrimeFaces dialog framework works, there's no way to prevent that. You could at most remove the pfdlgcid value from the session when the dialog is about to returns to the parent. However, when the dialog is closed by pure JS means, then this is also bypassed.
All in all, if you really, really, want to avoid the enduser being able to open the dialog individually, then you can't go around the "traditional" <p:dialog> approach.
I need to provide a file-download feature where the web server retrieves the file from another source (via HTTP) and simultaneously streams it to the browser. I am guessing that using MVC's Controller.File ActionResult will not work, but I wrote a prototype like this anyway:
public ActionResult Download()
{
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create("http://somewhere/somefile.pdf");
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
Stream stream = webResponse.GetResponseStream();
var mimeType = "application/pdf";
var fileName = "somefile.pdf";
return File(stream, mimeType, fileName);
}
This works fine, but there is no way to call Close() on the HttpWebResponse and Stream after the return statement. The help on the HttpWebResponse.GetResponseStream method says, "You must call either the Stream.Close or the HttpWebResponse.Close method to close the stream and release the connection for reuse. It is not necessary to call both Stream.Close and HttpWebResponse.Close, but doing so does not cause an error. Failure to close the stream will cause your application to run out of connections."
Should I create an HttpHandler and manually read bytes from the source stream and write them out to the response, along the lines of this or this? Is there another approach I'm not aware of?
While I'm not directly familiar with trying something like this, my first though was to do what you suggested in regards to reading in the stream, closing the connection, then returning the bytes as the response. Being a stream, I don't know how you can get around leaving it open for the sake of returning its contents as you do in your prototype, but then being able to close it when you're done.