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";
Related
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.
I have a method in my C# codebehind that connects to SQL and creates a CSV file based upon the SQL data. This method is called when the user clicks a particular button on the website. I am trying to get this same method to also prompt the user to "open" or "save as" after the CSV file is generated.
I have tried the following:
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.AddHeader("Content-Disposition", "attachment; filename=" + "C:\\temp\\report.csv" + ";");
Response.TransmitFile("C:\\temp\\report.csv");
The user does get prompted to "open or save as". In either case, the data in the CSV file also contains the entire source code of my Default.aspx. What do I need to do to my code to prevent that extra data from being appended onto my CSV? TIA...
You can call response.End() to prevent further processing by your page.
You need to clear the Response first - Response.Clear();
response.Clear();
...
response.AddHeader("Content-Disposition", "attachment; filename=" + "C:\\temp\\report.csv" + ";");
Response.TransmitFile("C:\\temp\\report.csv");
After downloading a file from a webpage, I can't do anything on that page. It is like the page is not working anymore. When I click on other button, there is no post back.
Here are my codings.
filename= "test.txt"
Response.AppendHeader("content-disposition", "attachment; filename= " + fileName);
Response.WriteFile(Server.MapPath("~/" + fileName));
Response.End();
So I try to comment Response.End() but it is still not working.
What can be the possible problems?
UPDATE
Go to this link to see how I solved it.
My problem looks like this.
I have a grid with documents (Id's). Now when somebody clicks at a row I would like to allow him to download or show that document. But to make it esier let's say that I would do this on a button click. I tried two approaches but none of them worked form me.
I tried to response.binarywrite on the button click:
Response.Clear();
Response.ClearHeaders();
Response.ClearContent();
Response.ContentType = "application/postscript mime";
Response.AddHeader("content-disposition", "attachment; filename=test.ps");
Response.AddHeader("content-length", _excuteGetDocumentResult.Length.ToString());
Response.ContentEncoding = new System.Text.UTF8Encoding();
Response.BinaryWrite(_excuteGetDocumentResult);
But nothing happens and when I try to modify this code I usually get some javascript errors saying sommething about changing the response...
The socond approach was opening new window and on page load adding the code above.
<asp:Button Text="ShowResult" OnClientClick="radopen('ShowResult.aspx','ShowDocumentDialog'); return false;"
runat="server" />
The socond approach works but my opened window still exists after saving or canceling the explorer saving file dialog window. I tried to add some javascript to close it but it only works where there is no response.binarywrite on the load page...
Any ideas how I can achive what I want?
In method 1.
try Response.End(); after Response.BinaryWrite(_excuteGetDocumentResult);
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