I have a button on my asp.net page that does a postback, creates an Excel file, clears the response stream and writes the file. The user can then open or save the file user the brower's standard dialog.
This works great, I based the code on this:
http://www.adventuresindevelopment.com/2009/05/27/how-to-export-data-to-excel-in-aspnet/
As the file to be created takes quite a long time I have created a loading panel, just a hidden DIV, and set this to visible when the button is clicked.
But my problem is how to hide this DIV when the export has finished? I just cannot find a way of doing it. I need something like an event that fires when the file has been completely transfered to the browser.
Is this possible? Any help most appreciated.
Thanks,
AJ
What I'd do, long story short :
When the user clicks the "Download" button, use AJAX to call a
processing page asynchronously. This page will generate your Excel
document and store it in a temporary location
When the AJAX request is done, hide the "Loading" panel, and
redirect the user to a download page. Ideally, you should redirect
to a generic (.ashx) handler that opens the file, sets some headers,
streams the temporary file to the user, and deletes the file
afterwards.
Now in more details :
For step one, you should have some temporary folder where you have read and write access. Using the system temp folder is fine, so you could use Path.GetTempFileName. Here is an example of what you could write in an ashx handler :
public class Handler1 : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
string fName = Path.GetTempFileName();
context.Response.ContentType = "text/plain";
try
{
// Generate the Excel document
GenerateExcelInFile(fName);
// Store the file name in session for later use
context.Session["ExcelGeneratorFileName"] = fName;
// Send confirmation to the client
context.Response.Write("ok");
}
catch (Exception e)
{
context.Response.Write("error");
// TODO : Do some logging
}
}
// SNIP : IsReusable
}
After that, use your favorite JS framework to request that handler, and test the returned string. If it is "ok", you call the part two handler :
public class Handler2 : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/excel";
// Make sure the browser will show a "save as" dialog to the user
context.Response.AddHeader("content-disposition", "attachment; filename=Export.pdf");
string fName = context.Session["ExcelGeneratorFileName"] as String;
if (fName != null && File.Exists(fName))
{
// Stream the excel file to the response
context.Response.WriteFile(fName);
// Remove the file
File.Delete(fName);
}
}
// SNIP : IsReusable
}
You can call this page in javascript simply using a window.location = url. The content-disposition header will tell the browser that this URL should not be displayed, only downloaded, so your user should stay on the download page.
Related
I am delivering a jasper report as a PDF for download. But when I do it I become unable to make the page to reload or redirect.
The page that causes the download to take place uses a form submission to start the file download. The answer provided by Govinda Sakhare was posted before I made this clarification. However his answer could be implemented with little work as the form only has one choice (big or small).
The function that handles the server response is below:
private void generateReportPDF(JasperReport jasperReport, List<? extends Object> data, Map<String, Object> parameters, HttpServletResponse resp) throws Exception {
byte[] bytes = null;
if( data == null ) {
bytes = JasperRunManager.runReportToPdf(jasperReport, parameters, reportDao.getConnection());
}
else {
bytes = JasperRunManager.runReportToPdf(jasperReport, parameters, new JRBeanCollectionDataSource(data));
}
resp.reset();
resp.resetBuffer();
resp.setContentType("application/pdf");
resp.addHeader("Content-Disposition", "attachment; filename=" + jasperReport.getName() + ".pdf");
resp.setContentLength(bytes.length);
ServletOutputStream ouputStream = resp.getOutputStream();
ouputStream.write(bytes, 0, bytes.length);
ouputStream.flush();
ouputStream.close();
return;
}
Because of this function, my controller is unable to redirect the page and I get the following error when I try to.
SEVERE: Servlet.service() for servlet [myapp-dispatcher] in context with path [/myapp] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed] with root cause
java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
I want to reload the page. This is on a label printing screen and after the file download, I want the page to reload/redirect to itself so that labels which were printed are displayed in their new spot. Since the controller is unable to redirect, I have to modify the original process which is handling the response or implement JavaScript to occur after the file has been dealt with.
This question asks about catching downloads and responding to them, but does not involve spring so the answers would require more changes than I want to make.
Using one of the answers from the question I managed to make a workaround but its not satisfactory to me.
$(document).ready(function() {
$('#printBtn').click(function() {
if(confirm('<spring:message code="print.alert.confirm"/>') ? true : false)
{
window.addEventListener('focus', window_focus, false);
function window_focus(){
//remove buttons
var elem1 = document.getElementById('printBtn');
elem1.parentNode.removeChild(elem1);
//elem1.parentNode.replaceChild(newbutton);
var elem2 = document.getElementById('clearBtn');
elem2.parentNode.removeChild(elem2);
var elem3 = document.getElementById('restoreBtn');
elem3.parentNode.removeChild(elem3);
document.getElementById('button_spot').innerHTML = "The page will reload after you get the file.";
//watch for page to lose focus due to download dialog box
window.addEventListener('focusout', pageNoFocus);
function pageNoFocus(){
//watch for page to resume focus
window.removeEventListener('focusout', pageNoFocus);
window.addEventListener('focus', pageFocus);
function pageFocus(){
window.removeEventListener('focus', pageFocus);
location.reload();
}
}
}
return true;
}
return false;
});
$('#clearBtn').click(function() {
return confirm('<spring:message code="print.alert.clear"/>') ? true : false;
});
$('#restoreBtn').click(function() {
return confirm('<spring:message code="print.alert.restore"/>') ? true : false;
});
});
You can use the following snippet to download the file and redirect to some URL.
Download
<a href="downloadfileURL" target="_blank" id="downloadFile" />
<!-- change href with Spring mapping which will download the file -->
On click of Download link, it will click anchor which points to the actual
URL.
The file will be downloaded, followed by that it will redirect to the window.location.href
$("#download").click(function () {
$("#downloadFile")[0].click();
window.location.href = "/abc.html"; // change to the desired URL
});
If you download the file via form submission use below snippet.
$("#formId").click(function (e) {
e.preventDefault();
$("#downloadFile")[0].click();
window.location.href = "/abc.html"; // change to the desired URL
});
I'm new to Javascript. I want to add an icon to all my project's web pages. This icon changes accordingly to whoever logs in in my page. What i'm trying now is, in master page load (when i have all of its data in codebehind, including his chosen icon), i introduced a js function to change icon:
(function icon(image) {
var link = document.querySelector("link[rel*='icon']") || document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.hre = image;
document.getElementsByTagName('head')[0].appendChild(link);
})();
(adapted from here: Changing website favicon dynamically)
im trying to call this function with Page.ClientScript.RegisterStartupScript() method:
(protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
//...
UserModel usuario = (UserModel)Session["usuario"];
//...
Page.ClientScript.RegisterStartupScript(this.GetType(), "icone", $"icone({(Bitmap)new ImageConverter().ConvertFrom(usuario.imagem)});", true);
}
}
The method is simply not executing(its not returning error either, it just "jump it"), and i sincerely have no idea why.
OOOOR, there may be a better way to do it, but i just cant figure it.
For the record, i DONT have the icons/images on a folder. I MUST get them form the database.
(I will later add validation for when user has no image in database. Assume for now it will never be null).
A few things need to be edited:
First, think you got the parameters for RegisterStartupScript not set as required. The 3rd value should be the javascript code to run. You can add the entire script as a variable (as indicated here: https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.clientscriptmanager.registerstartupscript?view=netframework-4.7.2), or you can add your function in <script>...</script> tags to the HTML page, and then just call the function from within RegisterStartupScript (as the 3rd parameter value)
Secondly, there's a typo in your JavaScript function:
link.hre = image; should be link.href = image;
Thirdly, (this part might require some work) the image has to be a URL (string) to the actual image (Not the binary Bitmap)... you might first have to save the image to the web server as a .jpg or .png and use its URL to there, or you have to convert the image to Base64 and add the image via Data URLs https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
Finally, you modified the JavaScript from the original example (you mentioned) In this case, you want to send the image URL as a parameter to the function icon However, the icon function is encapsulated. So the function is basically in a Private scope. Remove the encapsulation around the function if needed to be called from another function on the page... hope this make sense.
Instead of using Javascript, you could simply link your shortcut-icon to a handler, that immediately returns the image. (The following code is not tested, it's there to describe the basic process!)
In your page:
<link rel="shortcut icon" type="image/x-icon" href="/userimage.ashx">
userimage.ashx:
<%# WebHandler Language="C#" Class="UserImageHandler" %>
userimage.ashx.cs:
using System;
using System.Web;
public class UserImageHandler : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "image/png"; // or image/jpg, image/bmp, ...
var image = FetchImageFromDatabase(context.User.Identity.Name); // your logic for fetching the image from the database
// You could also return a default image here, if the user has not selected one.
context.Response.Write(image); // Write the binary data to the response stream.
}
public bool IsReusable {
get {
return false;
}
}
}
I have a gridview with a list of pdf files. When the user clicks a pdf, it displays the file inline on the page. I want to execute some javascript after the pdf has been loaded but I cannot get this to work. The issue is that the pdf loads after everything else, so the load event fires before the pdf begins to load.
My first approach was to use an iframe. The inner page would retrieve the file and write the data to the response. As mentioned previously, the load event occurred before loading the pdf and I need it to trigger after. The current code uses a generic handler ashx to load the pdf inline. How do I trigger an event to execute javascript, after the pdf data is loaded server side from the ashx generic handler?
Aspx page:
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "View")
{
int index = Convert.ToInt32(e.CommandArgument.ToString());
string Id = GridView1.DataKeys[index].Value.ToString();
HtmlGenericControl myObject = new HtmlGenericControl();
myObject.TagName = "object";
Panel1.Controls.Add(myObject);
myObject.Attributes.Add("data", "GetPdf.ashx?Id=" + Id);
}
}
Generic handler ashx:
public void ProcessRequest(HttpContext context)
{
System.Diagnostics.Debug.WriteLine("GetPdf.ashx started");
string Id = context.Request.QueryString["Id"];
byte[] data = GetPdf(Id);
context.Response.ClearContent();
context.Response.ContentType = "application/pdf";
context.Response.AppendHeader("Content-disposition", "inline");
context.Response.AddHeader("Content-Length", data.Length.ToString());
context.Response.BinaryWrite(data);
System.Diagnostics.Debug.WriteLine("GetPdf.ashx is done");
context.Response.End();
}
Have you tried setting an event handler for the object tag's onload event? I'm not sure if that will work across all browsers, but I also don't know which browsers you require it to work on.
Worst-case scenario you could use setTimeout to rapidly poll for the PDF's existence.
Here's a previous answer that may help you with both aspects.
I am working in Spring 3, Java, JSP, javascript, and jquery, using Ajax occasionally. I have server functions that generate a PDF; I have a new requirement to show a "preview" of a PDF document.
I have code that generates the PDF document, that works fine. I can show it by hacking my source to display it in the place we normally show the un-watermarked completed document, so I know the generation of the PDF is working.
What I now want to do is display that PDF in its own tab as the result of clicking on a button (or link) on our web page. There are a few restrictions:
I have a bunch of data to pass up to the controller from the web page, data that it needs to generate the PDF. We have code that does this through a POST method, and use Ajax to post the necessary data.
It would be inconvenient for the PDF to show up in the same window as the button clicked to show the PDF; a popup asking if the user wants to download or view elsewhere is fine. The users aren't sophisticated enough to depend on theiri knowledge of the 'back' button here. So we want the PDF to show up elsewhere, preferably on another tab in the window but another entire window would be ok.
I have the following in my controller at the moment:
response.setContentLength(pdfGenerated.length);
response.setContentType("application/pdf");
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
response.setHeader("Content-Disposition", "attachment;filename=\"Preview.pdf\"");
ServletOutputStream out = response.getOutputStream();
out.write(pdfGenerated); // (encodedPdf);
out.flush();
out.close();
The ajax call looks like this:
$("#generatePDFPreview").live("click", function() {
var gridData = getCorrectedGridData();
var valid = validateContractContent(gridData);
if (valid) {
// the call below saves the contract data and then generates its PDF
$.ajax({
url: getModelObject("generatePDFPreviewURL")
,type:'POST'
,data: {'editedContents':JSON.stringify(gridData)}
,datatype: "application/pdf"
,async: false
,success: function(data) {
if(data != null && data.length>0 && data != "Error") {
//data must be contract id...use it to build the complete URL.
//window.location.href = getModelObject("deliveryScreenURL") + data;
window.open("data:application/pdf;base64, " + data);
} else {
alert("PDF preview not generated...Data returned is not ok. Please try again or contact Sales Support.");
}
}
});
}
});
I have tried different things here; I have left off the 'success' function entirely; I have tried encoding the data (base64) and returning that, and using data:application/pdf, etc., but that failed -- I have some evidence that the PDF data was too long for this, but am not sure (it was 85k-90k, the URL string stopped at something like 32784).
I am not worried about whether my user has the PDF reader installed. They must have it installed to use this and other parts of the application.
It is frustrating to be so close; all evidence is that we have most of the pieces in place, it's should just be a matter of telling the browser that we want it to use the PDF Reader to handle these bytes.
Can someone point us to a method, or point out what's wrong with what we've got now?
In my project, I have a file that I want the user to download. When they click on the link, I want a popup window to display "Your download will shortly, if it doesn't start click here". After a few seconds, it will then close and the actual file download will then display.
I know to achieve the window closing you'll use:
window.setTimeout(function(){window.close()}, 5000);
But I'm not sure how you would call the download once the window has closed?
Cheers for any help!
In simple way, use window.open() to start download file.
Direct link
<script type="text/javascript">
setTimeout(function() {
window.open("myfile.doc");
},3000);
</script>
Okay so I don't know your platform so will give an ASP.NET version. If you are using something else then I have commented so you should be able to adapt to your platform.
EDIT: Now know user is using PHP so added code snippet for PHP (not robust but I'm no PHP dev)...
1) SAME FOR PHP/ASP Are you getting a file that will not be displayed by the browser automatically? i.e. .js will be shown as is but an exe will probably trigger a file download dialog (someone correct me if wrong please and I'll update)
If your files are always going to be i.e. .exe then you could probably just get away with:
$("body").append("<iframe src='http://www.targetsite.com/files/thefilename.exe'></iframe>");
but more likely you will be using a parameter to find the right file (and hide direct download
$("body").append("<iframe src='http://www.targetsite.com/downloader/?file=1234-1234-1234'></iframe>");
in some setTimeout function.
If the filetypes are unknown then I suggest pointing the above code at a script file (.ashx, php, etc) that writes the file byte stream to the http response.
FOR PHP:
<?php // Demo - send a (binary) file
$file = "ireland.jpg";//here you would use the query string parameter of the above
//ajax/iframe request eg file=1234-1234-1234 to find image in db
$fp = fopen($file,"r") ;
header("Content-Type: image/jpeg");//this would need to be modified to either show right content type or you could
//set it to Application/force-download
while (! feof($fp)) {
$buff = fread($fp,4096);
print $buff;
}
?>
WARNING Be careful with the above code. It occurred to me you might pass in filename directly which I'm pretty sure someone could use to get files in other places in your app without careful attention
FOR ASP:
I have included an example ashx (generic handler) solution:
aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Wardle.PdfGenerator;
using System.IO;
public partial class user_account_iframedownloader : System.Web.UI.Page
{
private IInformixRepository _rep;
//this page gets loaded into an iframe so we can do downloads while using ajax
protected void Page_Load(object sender, EventArgs e)
{
//write bytes out here i.e. see after for methods
}
}
Example byte output methods (you just need to do File.getBytes or something - my code is quite complicated so 'excercise for reader'
public static void PdfOutput(byte[] pdfData, string filename)
{
HttpContext.Current.Response.ContentType = "Application/pdf";
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename);
HttpContext.Current.Response.BinaryWrite(pdfData);
}
public static void PdfZipOutput(byte[] zipData, string filename)
{
HttpContext.Current.Response.ContentType = "Application/zip";
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename);
HttpContext.Current.Response.BinaryWrite(zipData);
}