So I know that this is common problem, page freezes (any buttons visually are clickable but doesn't perform any action) because it doesn't close some request after file download.
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.ContentType = "text/txt";
response.AddHeader("Content-Disposition", "attachment; filename=" + TreeView1.SelectedNode.Text + ";");
response.TransmitFile(TreeView1.SelectedNode.Value);
response.Flush();
response.Close();
HttpContext.Current.ApplicationInstance.CompleteRequest();
This code is in TreeView SelectedNodeChanged event, and this is a problem.
For example if will put that transmit code in some button, I can fix freezing of page by adding OnClientClick="javascript:setFormSubmitToFalse()" to the button and this small JavaScript fixes page freezing after download.
<script type="text/javascript">
//Fix page freeze after download dialog
function setFormSubmitToFalse() {
setTimeout(function () { _spFormOnSubmitCalled = false; }, 3000);
return true;
}
</script>
But I don't know how to fix this if I'm clicking on TreeView node. TreeView doesn't have OnClientClick, I also tried to run that JS function from code behind right before and after transmit code Page.ClientScript.RegisterStartupScript(this.GetType(), "CallMyFunction", "setFormSubmitToFalse()", true);
but it doesn't help, page is still freezing after file download.
Any ideas how can I fix this? Thanks.
This is webpart (almost asp.net page) on sharepoint site.
Edit:
I'll try to explain better my situation and what I'm trying to achive.
So my main goal: There is a shared folder with documents in local network, users that will be using this app should not have access to this shared folder, only entity that is running App pool has access to it.
I am making some SQL request that gives me path of some subfolder. I am building treeview of that subfolder and when user clicks on some file in treeview SelectedNodeChanged event shots and I perform transmitting file as app pool identity
SPSecurity.RunWithElevatedPrivileges(delegate()
{
/* transmit file code */
});
Everything is working fine but if I understand right, something on CLIENT side probably freezes page after TransmitFile shoots and prevents from sending any other post request to server, that's why my little JS solution worked when it was performed OnClientClick.
This is original how author of that fix explained that, but I don't know how to implement this into treeview https://stackoverflow.com/a/17186011/5805492
So solution was pretty simple but a little bit tricky.
Nodes doesn't have onClientClick event, but we can add JS click event to any page element, right? So here is the trick, when creating TreeView just add click to actual text of the node with help of Span:
...
TreeNode fileNode = new TreeNode
{
//before
//Text = file.Name,
//after
Text = "<span onclick=\"javascript:setFormSubmitToFalse();\">"+file.Name+"</span>",
Value = file.FullName,
};
...
And in transmitting file parse text of the node to have normal name of the file:
response.AddHeader("Content-Disposition", "attachment; filename=" +
TreeView1.SelectedNode.Text.Replace("<span onclick=\"javascript:setFormSubmitToFalse();\">", string.Empty).Replace("</span>", string.Empty) + ";");
Now everything is working and page is not freezing anymore after downloading.
I'm not sure I really understand your situation, but your app is likely being frozen due to the fact that the UI thread is hung up on code that you are trying to run synchronously.
In order to prevent that from happening, you need to run the logic asynchronously.
public async void OnSelectedNodeChanged(object sender, EventArgs e)
{
await Task.Run(() => {
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.ContentType = "text/txt";
response.AddHeader("Content-Disposition", "attachment; filename=" + TreeView1.SelectedNode.Text + ";");
response.TransmitFile(TreeView1.SelectedNode.Value);
response.Flush();
response.Close();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}).ConfigureAwait(false);
}
This is an async void method, which means the code that fires this event will not wait for it to finish, nor will it know when it has finished.
This will prevent the thread from locking your UI, but if the user has the ability to "rapid fire" these events by clicking all around your UI, it may not be desirable to write the code this way.
Regardless, you will likely need some form of async logic to accomplish what you are wanting.
Related
So I'm working with this form which, at the end, generates a Word document for the user to download, fires off some emails, and then displays a success panel for the user. The problem I'm having is that the .Visible flags don't end up getting changed whenever I call the downloadWordFile() method. Is there something in that method that would be messing with my ability to change visibility of ASP panels? I've tried removing almost every part of this to see where the issue is popping up and I haven't been able to make heads or tails of it. Everything else works fine, it's just this method causing the issue. Thanks in advance for your help!
protected void btnSubmit_Click(object sender, EventArgs e)
{
pnlForm.Visible = false;
pnlSuccess.Visible = true;
email();
adminEmail();
downloadWordFile();
}
protected void downloadWordFile(){
Response.ContentType = "application/msword";
Response.ContentEncoding = System.Text.UnicodeEncoding.UTF8;
Response.Charset = "UTF-8";
Response.Write("<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'>");
Response.Write("<head>");
Response.Write("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"\"text/html; charset=UTF-8\"\">");
Response.Write("<meta name=ProgId content=Word.Document>");
Response.Write("</head>");
Response.Write("<body>");
Response.Write("<div class=Section2>");
Response.Write(buildForm());
Response.Write("</body>");
Response.Write("</html>");
Response.AppendHeader("Content-Disposition", "attachment; filename=" + "Generic.doc");
HttpContext.Current.Response.Flush();
}
EDIT 1: I ended up going a different route entirely. Rather than trying to switch panels, generate emails, and generate word files all in one go, I made the submit button switch the panels, and the success panel now shows a preview of the word document to be downloaded and provides the user a chance to make changes. There is a download button on the success panel that generates the emails and downloads the word document. This required minimal changes to the way the code was already set up and solved another problem I was having with the email generation.
A response can only be one thing. You can't respond with page content and a file. You are setting the response to a file so that is how the browser handles the response.
I would handle this situation by the calling a JavaScript function in the button's click event that downloads the file via an IFrame.
First you would need a "download file" page. In the OnLoad event of that page you can call email();, adminEmail(); and downloadWordFile();
The JavaScript click event handler would look something like this.
function DownloadFile() {
var downloadFrame = document.createElement("IFRAME");
if (downloadFrame != null) {
downloadFrame.setAttribute("src", 'DownloadFile.aspx');
downloadFrame.style.width = "0px";
downloadFrame.style.height = "0px";
document.body.appendChild(downloadFrame);
//Set the visibility of pnlForm and pnlSuccess
}
}
EDIT:
Per your comment, I've had to do the same thing. I solved it by posting the form as normal and assembling the form values into a query string. Then use ScriptManager.RegisterStartupScript to add a script that calls the DownloadFile() function when the page loads. The DownloadFile() function takes the query string as a parameter and the DownloadFile.aspx page uses it to get the form data.
So the click event handler would be updated like this.
function DownloadFile(queryString) {
var downloadFrame = document.createElement("IFRAME");
if (downloadFrame != null) {
downloadFrame.setAttribute("src", 'DownloadFile.aspx' + queryString);
downloadFrame.style.width = "0px";
downloadFrame.style.height = "0px";
document.body.appendChild(downloadFrame);
//Set the visibility of pnlForm and pnlSuccess
}
}
I'm trying to have a user click a button on a web page to download a CSV file, but I'm having problems sending the correct data to the user. In addition, once the file has been downloaded, I'd like the page to remain "active" i.e. the page can continue to trigger events to the server, such as clicking the Download button again.
This is the code I'm currently using, pieced together from various SO questions:
var sb = new StringBuilder();
string fileName = "data.csv";
// Build CSV file...
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.AddHeader("content-disposition", string.Format("filename={0}", fileName));
HttpContext.Current.Response.ContentType = "text/csv";
HttpContext.Current.Response.Write(sb.ToString());
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.End();
This works so far as presenting the user with an option to open or download the file, but the file also contains the entire markup of the aspx file after the requested data, and the page is left completely inactive.
I'm guessing the problem is with the last two lines of the above code. I've tried using ApplicationInstance.CompleteRequest instead of Response.End, but this doesn't seem to change the outcome. The page is still inactive and the file still has the markup at the end. Can anyone help?
In case it makes any difference in the behaviour of Response, the page is an WebPartPage in SharePoint 2010.
Set the line
HttpContext.Current.Response.AddHeader("content-disposition", "filename={0}", fileName));
to
HttpContext.Current.Response.AppendHeader("content-disposition", String.Format("attachment;filename={0}", fileName));
And set the following property on your button
downloadButton.OnClientClick = "_spFormOnSubmitCalled = false;";
_spFormOnSubmitCalled is a javascript variable that SharePoint uses to stop multiple form submits and it is being set to true when you click the download button.
try this
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
HttpContext.Current.Response.ContentType = "application/CSV";
I have the following code to download a file from the server to the client, that occurs when the button btnSavetoDB_ExportToExcel is clicked, after that I want to have the button disable, how can I achieve that?
string fileName = newFN + ".xlsx";
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);
Response.TransmitFile(DestFile);
Response.End();
btnSavetoDB_ExportToExcel.Enabled = false;
I noticed that the button did not become disable when placed the above code (Response), cause I had another code there and the button became disable with it. So it must be something related with the Response.
EDIT:
The button is within a ModalPopupExtender in case it matters.
The reason for this is that if you disable the button with server code you need to return the response to the browser window.
Here you return a stream that is captured by the browser as a file instead of to the browser window so the current display is never updated.
To get around this you can implement a javascript line on the button in the html itself.
You can use Attribute.Add() in code at Page_Load, f.ex:
btnSavetoDB_ExportToExcel.Attribute.Add("onclick","this.disabled=true")
There at least two possible solutions.
1) Instead of writing a file to the response you can generate file in the temp directory on the server and give the user the link to this file (or redirect to this link).
2) If you want use writing to the response, you can create new Generate.aspx page that writes the file to the response in its Page_Load method.
if (Request.QueryString["ID"] != null)
{
//find file by its ID
...
Response.AddHeader("Content-disposition", "attachment; filename=" + fileName));
Response.ContentType = "application/octet-stream";
Response.WriteFile(fullFileName);
Response.End();
}
On page with your btnSavetoDB_ExportToExcel in Page_Load add this code:
var script = #"<script language=JavaScript>function Export(fileID)
{
var iframe = document.createElement('iframe');
iframe.src = 'Generate.aspx?ID='+ fileID;
iframe.style.display = 'none';
document.body.appendChild(iframe);
}
</script>";
Page.RegisterStartupScript("Startup", script);
And then just call javascript Export method to send file to the user.
As mentioned in the other answer, the button should be disabled in client side (since you are using Response.End() in your code, ASP.NET will never render the page with the button having Enabled=False.
In your .aspx file add
<asp:Button ID="btnSavetoDB_ExportToExcel" OnClientClick="this.disabled=true;" [..] />
But also note that this might not be a good idea altogether - what if the user accidentally clicks "Cancel" from the file download window?
A better solution (understanding that you want to prevent the user from clicking the button multiple times) is to add a timer to re-enable the button:
<asp:Button ID="btnSavetoDB_ExportToExcel" OnClientClick="var a=this; a.disabled=true; window.setTimeout(function() { a.disabled=false; }, 5000)" [..] />
Value 5000 is the time in miliseconds after which the button will be re-enabled.
I have found the solution to my problem, credit goes to: Dave Ward (click here)
<asp:Button ID="btnSavetoDB_ExportToExcel" runat="server"
OnClientClick="this.disabled = true;"
UseSubmitBehavior="false"
Text="Save results to DB and export to Excel"
onclick="btnSavetoDB_ExportToExcel_Click" />
this does the trick:
OnClientClick="this.disabled = true;"
UseSubmitBehavior="false"
I'm having trouble getting my ASP.Net page to return CSV on submit being clicked. Here's the asp button definition I have for the submit button in Form.aspx:
<asp:Button id="submitreport" name="submitbutton" text="submit" OnClick="Report_Submit" runat="server" />
And this is the corresponding function in Form.aspx.cs:
public void Report_Submit(object sender, EventArgs e) {
Debug.WriteLine("GETS HERE?");
Response.Charset = "UTF-8";
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetAllowResponseInBrowserHistory(true);
Response.ContentType = "text/csv";
Response.AddHeader("Content-Disposition", "attachment; filename='" + DateTime.Today.ToString() + ".csv'");
Response.Write("test,output");
Response.End();
}
When this is ran in Visual Studio I don't even see the debug print line. Does anybody know what's wrong with my setup?
Edit: I know for certain that the pages are set up right because if I put a breakpoint on my empty Page_Load function in Form.aspx.cs VS does break there. Besides that the breakpoint at the debug write line is skipped over on the form submit and the same page is returned again.
Update By creating a new project with just the button and the handler the Report_Submit() function is called and the CSV file is correctly generated. Since that narrows it down a little bit, does anybody know what could be going on in my other VS 2008 project that's causing this not to work?
I found a pretty good work around that returns a .csv file (which also shouldn't be quoted in my header creating function calls, but that wasn't the problem). In addition, the shouldn't specify a name since ASP.NET fills that in, but that also wasn't the problem.
Moving just the relevant form processing code into its own project worked just fine, but my project was too large to manage that.
The workaround I found useful is to just move the button handling function into Page_Load() and use:
if(Request.HttpMethod == "GET") {
Debug.WriteLine("this is just a request for the page"):
}
else if(Request.HttpMethod == "POST") {
Debug.WriteLine("processing the form");
//rest of code...
}
else {
//some HTTP action that doesn't matter
}
to distinguish page requests (GET) from users entering the form (POST requests).
EDIT 2
It appears that moving the object tag in the Dom is the cause of the problem. I added an iframe directly to the page and it works fine without any problems.
However, when I move that iFrame into a modal dialogue box (http://www.ericmmartin.com/projects/simplemodal/) the PDF disappears (the object tag no longer renders correctly).
So it appears that it's no longer a question about my handler, but more a question about why moving the "object" (or embed) tag in the DOM (which lives inside an iFrame) causes it to "blank-out."
Also, when the pdf is moved from the modal dialogue back to its original position, it appears correctly. So, perhaps I should focus more on the modal dialogue itself.
Thoughts? Thanks for your suggestions thus far.
EDIT 1
So I've made some modifications for testing.
I've got the iframe to output an object tag for pdf requests along with the server time.
Response.AddHeader("content-type", "text/html");
WebClient client = new WebClient();
Response.Write("<html><head></head><body><h1>"+ DateTime.Now.ToString() + "</h1><object height='100%' width='100%' name='plugin' data='" + Request.Url.ToString() + "&embed=true' type='application/pdf' /></body></html>");
Response.Flush();
Response.Close();
Response.End();
Now I get a page with the current time correctly, but the object only displays the PDF the first time after I publish the aspx page. So it appears to be some sort of caching issue? Except that the object isn't loading anything (not even the previously loaded PDF).
If right click on the iframe and refresh the page, the object loads up fine. (The same is true if I use an embed tag).
Original Question
I know there are a lot of questions on this...
streaming PDF data through an ASPX page
Server generated PDF not displaying in IFrame on aspx page on some (but not all )PCs
Displaying a PDF Document in ASP.net page
But they either weren't answered, or the answer didn't work.
Environment
.Net 4
Adobe 9.3.4
IIS 5.1
XP sp3
VS 2010
IE 8.0.6001.18702
Background
The pdf's I'm streaming come from a storage repository where the files don't have any extensions (this is done for security). I look up the file in the database and stream it back to the client via the following code:
Response.Clear();
WebClient client = new WebClient();
Byte[] buffer = client.DownloadData(sPath);
Response.AddHeader("content-length", buffer.Length.ToString());
Response.AddHeader("content-disposition", "inline;filename=" + fileName);
Response.AddHeader("expires", "0");
Response.AddHeader("Content-Type", "application/pdf"); //this is usually dynamic to support other types (doc, xls, txt, etc.)
Response.OutputStream.Write(buffer, 0, buffer.Length);
Response.Flush();
Response.Close();
Response.End();
This works for every file type (doc, txt, xls, html) when used directly in the browser or in the iframe (displayed as a modal popup) with the exception of pdf files. They do not work reliably when accessed via the iframe, but work fine when accessed directly in the browser.
The only time it does work is the first time I request a document after I publish the aspx page that is serving these files. All subsequent hits return a blank page (even from new tabs or browser windows). Firefox reliably displays the pdf every time regardless.
Attempted Solutions
I've tried various ways I of streaming the file:
Response.TransmitFile(sPath);
Response.WriteFile(sPath);
//As well as some others
I've tried adding .pdf to a parameter at the end of the request
http://www.myurl.aspx?File=test.pdf
I've tried making the URL unique by adding a time stamp
http://www.myurl.aspx?File=test.pdf&Unique=Friday__September_17__2010_12_02_16_PM
Un-Attempted
I've read about IIS compression causing problems, but it was for a newer version of IIS.
Didn't try using embed tag since I would like to stick to the iFrame if possible (The existing infrastructure uses it).
Any help would be greatly appreciated!!!
Thanks.
I had a similar problem that arose when the PDFs were streaming over SSL (IE only, FF didn't exhibit the issue) that was only solved by doing the following:
Response.ClearHeaders();
Response.ClearContent();
Response.Buffer = true;
Response.AppendHeader("Content-Disposition", "inline; attachment; filename=Filename.pdf");
Response.ContentType = "application/pdf";
Response.WriteFile(path);
Response.Flush();
Response.End();
So I gave up and decided to use a standard IE popup window instead.
window.open(URL, 'Window', 'height=' + pageHeight + ',width=' + pageWidth + ',top=0,left=0,resizable');
I had to render the pdfs in an object tag and everything else inside an iframe within the popup for it to work, but it works...
if (sDocType == "pdf")
{
Response.AddHeader("content-type", "text/html");
WebClient client = new WebClient();
Response.Write("<html style='margin:0px;padding:0px;'><head></head><body style='margin:0px;padding:0px;'><object height='100%' width='100%' name='plugin' data='" + Request.Url.ToString() + "&embed=true' type='" + zGetContentType(HttpContext.Current.Request.QueryString["docType"]) + "'><param name='src' value='" + Request.Url.ToString() + "&embed=true' />alt : <a href='" + Request.Url.ToString() + "&embed=true'>View</a></object></body></html>");
Response.Flush();
Response.Close();
Response.End();
}
else
{
Response.AddHeader("content-type", "text/html");
WebClient client = new WebClient();
Response.Write("<html style='margin:0px;padding:0px;'><head></head><body style='margin:0px;padding:0px;'><iframe frameborder='0' scrolling='no' height='100%' width='100%' src='" + Request.Url.ToString() + "&embed=true' /></body></html>");
Response.Flush();
Response.Close();
Response.End();
}
I'm not sure that you need to set the filename, especially if it doesn't have the .pdf extension. When streaming PDFs to browser in the past, I've always used this code:
Response.Clear();
Response.ContentType = "application/pdf";
Response.BinaryWrite(pdfBuffer);
Response.Flush();
Otherwise, there's a possibility that something has hosed over the registry settings for the application/pdf CLSID on the client computer.
I was able to solve a similar problem (pdf inside of modal window internet explorer)
by taking out the iframe. Instead, load the pdf into an html object element.
see the link below:
http://intranation.com/test-cases/object-vs-iframe/
To sum it up:
The setup that did not work:
jQuery floatbox, loads html fragment with iframe, load aspx page into iframe, load pdf into aspx page
The setup that now works:
jQuery floatbox, loads html fragment, append object element.
before appending the object element, set data
attribute of object element to the aspx page url