Postback Happening after Download when Clicking a Button or Link - c#

This is a problem I've had in the past, but I forgot if I ever solved it or not. I have it so when the user clicks on an asp:linkbutton it triggers a download for a file. File downloads successfully without a problem. However, after it downloads if the user clicks to download again or clicks a button a postback happens and the page refreshes, clearing out everything such as tables or text. How do I prevent this from happening?
Here's the code executing for the download.
string name = Path.GetFileName(filePath);
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=\"" + name + "\"");
Response.ContentType = mimeType;
Response.BinaryWrite(file);
Response.End();
NOTES:
The linkbutton control is NOT created behind the scenes in code, if that makes a difference.
The file does not download when clicking the link on the second time. Only a refresh happens.
This is part of a DNN module

First you need to save the information you want to keep before the post back happens. One of the ways this can be done is by using session. For example if you wanted to save the value of a text box you could say:
Session["TextBox1"] = TextBox1.Text;
Then you need to handle the post back in your page load function.
private void Page_Load()
{
//check if this is a post back
if(this.IsPostBack)
{
//restore your values
TextBox1.Text = (string)Session["TextBox1"];
}
}

Okay, I know the source of this problem , but I never found a solution for it. The reason this even shows up is because of a setting in DotNetNuke. Under Host Settings in the Advanced Settings tab if you look at Performance Settings there is a setting called Page State Persistence. If you set this to Memory like I did it will cause AJAX issues. It's even noted in the description. Setting it back to Page makes the problem disappear.
Reason I've kept it as memory is because my company's site was using Memory, however without my knowing it was switched back to Page. Now it's a non issue, but if anyone finds a solution for when it's set to Memory let me know! Otherwise, I'd suggest against using it unless it was fixed in newer versions of DNN.

Related

Page back not working as expected on BinaryWrite

I have a home/main menu page. There's a link to the form. If you run the form with inputs, you get Excel output. If you click 'open' to open that output in the browser, the back-button takes you to the home/main menu.
Which is understandable, since if the form page is X.aspx, the binaryWrite that outputs the Excel doesn't really change that.
The problem is that if there's a postback, the back button takes you to the form page. This is inconsistent. If I run the page or a drop down causes a postback, I really want the back-button to take me to the form field, so that I can run the excel sheet again.
Things I've tried.
I tried adding a location header.
e.ContentFileName = "ELTFile.Output.xls";
Before the response.Writing ...
private void SendFileToBrowser(DownloadFileEventArgs e)
{
if (!e.Cancel && e.FileObject != null)
{
this.Page.Response.Clear();
this.Page.Response.ClearContent();
this.Page.Response.AddHeader("Content-Disposition", string.Format("inline; filename={0}", e.ContentFileName));
this.Page.Response.AddHeader("Content-Length", e.FileObject.LongLength.ToString());
this.Page.Response.AddHeader("Location", e.ContentFileName);
this.Page.Response.ContentType = e.ContentType;
this.Page.Response.BinaryWrite(e.FileObject);
this.Page.Response.End();
}
}
And I tried
Response.Cache.SetCacheability(HttpCacheability.Public);
As well as
Response.Cache.SetCacheability(HttpCacheability.Server);
But basically, depending on whether or not I post back before I click the 'Submit' button to generate the Excel, I get the main menu (don't want) or the form page (want). I want the form page every time. Basically I want to tell the browser "I know it's not really a different page, but treat it that way, will ya please?"
You cannot directly add pages to a browser history.
You could make your application such that the form is always a page ahead of the excel output.
To do this, have a separate page to output the excel.
When you have the inputs to generate the excel, Response.Redirect to the generation page.
This will keep your input form in the browser history.
Note: you could redirect to the same page too if you liked, but that would need you to track it.

Refresh page content after Response.End()

I have a piece of code that generates a csv file dynamically. The user clicks a button, I prepare the string and then I do so something like:
MemoryStream stream = new MemoryStream(ASCIIEncoding.Default.GetBytes(sb.ToString()));
Response.Clear();
Response.ContentType = "Application/csv";
Response.AppendHeader("content-disposition", String.Format("attachment; filename={0}.csv", fileName.ToString()));
Response.BinaryWrite(stream.ToArray());
Response.End();
The user is then prompted to download or save the newly generated file.
The problem is that after this I have to refresh some data in the page. After the Response.End this is not possible anymore.
Any ideas on how to overcome the problem?
This is a common problem and there are lots of ways to solve it which all boil down to needing to create two separate requests to your server. The first one will request your CSV and the second will request your refresh.
With this in mind it's obvious that you can either:
Get the user to create both requests (by clicking separately on two links)
Use javascript to create both when the user only clicks once
So for example the link that sets off your CSV download could also call a javascript function which causes your data to refresh. The usual way to do this would be to use OnClientClick to call some javascript which will refresh your data.
However you can decide the best way forward based on your app.
Maybe clicking on a button will in a new window redirect user to a handler (ashx) which will return the stream / prompt user to save the file?
-or-
Update content first, then send file to a user.

Enable button after writing file to response

There is many similar questions but there is still no clear answer that is solving the problem taking some action after writing some stream to response.
I have a following situation:
On button click I am generating some excel file that I am going to write to response allowing user to download generated file. Imidietly after clicking the button, I am disabling it, to prevent double clicking this button. In Page-Load event handler I have following code:
GenerateBTN.Attributes.Add("onclick", "this.disabled=true;" + ClientScript.GetPostBackEventReference(GenerateBTN, "").ToString());
After Page_Load eventhandler, GenerateBTN_Click handler executes the code needed for generating the file and at the end of this method (handler) I am response writing generated file with following code:
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
Response.WriteFile(#"C:\Reports\" + filename);
Response.End();
The Save As dialog appears and user can download the generated file, but the problem is that the disabled GenerateBTN remains disabled. How to enable it afer Writing generated file to response? I understand that afer clearing current response I can not continue with the initial Response, but is there any way to solve this problem?
You can put an IFrame on the page and set it's visiblity to hidden. Have your button load the file in the IFrame and use Javascript to detect if the IFrame is still loading or not. When the loading is done, enable your button.
Can't supply a code example at the moment, but if you decide to go this route and need a sample, let me know I will update this answer.
Edit for 2nd answer
What you want to do is create a file like "Download.aspx" that you pass in the file name as a querystring parameter. This way, you can have your server get the file from a location outside of the Web Application's path and adjust the header to force a file download.
Download.aspx
using System.IO;
protected void Page_Load(object sender, EventArgs e)
{
string FileName = Server.UrlDecode(Request.Params["FileName"]); //Example: "MyFile.txt"
Response.AppendHeader("Content-Type", "application/force-download");
Response.AppendHeader("Content-Disposition", "attachment; filename=" + FileName);
Response.WriteFile(#"C:\MyFolder\" + FileName);
}
You would load the page by calling something like "Download.aspx?FileName=MyFile.txt"
You will need to add checks to make sure the file exists and the querystring parameter exists, but that should force the download and allow you to get the file from another location. When you use an ASPX page to serve the file, you can also do credential checks to see if the user is logged into your site (if you have login logic already) or log the download to a log file/database if you want to keep track of it. It gives you a lot more control over the download process.
As for the IFrame loading code, I'm not 100% sure how this works with a file download, but what I was originally thinking was something like this -- view source on: http://sykari.net/stuff/iframe.
Wrap the button in an UpdatePanel, then simply toggle its enabled property before and after the file work.
You can put an IFrame on the page and
set it's visiblity to hidden. Have
your button load the file in the
IFrame and use Javascript to detect if
the IFrame is still loading or not.
When the loading is done, enable your
button.
Can't supply a code example at the
moment, but if you decide to go this
route and need a sample, let me know I
will update this answer.
I've decided to use your suggestion.. but i still have some questions regarding this problem...
Is it possible to load .txt files in iframe?
Is it possible to load some files that are not included in web application's folder?
The problem with loading txt files in iframe is it does not trigger save as dialog to appear, instead of that file content is displayed inside that iframe.
For loading files into an iframe I've used following code:
HiddenFrame.Attributes["src"] = /GeneratedFiles/ + "test.zip";
You can see that I've had to use relative path and my file has to be included in web app's virtual folder.
What is the best javascript (jquery) eventhandler (function) to detect when Iframe has finished loading? I've used jquery function:
$("#ctl00_ContentPlaceHolder1_HiddenFrame").ready(function () {
if ($("#ctl00_ContentPlaceHolder1_HiddenFrame").attr('src') != '') {
$('#ctl00_ContentPlaceHolder1_GenerateSapFilesBTN').removeAttr("disabled");
}
});
But it appears that button is being enabled before Save As dialog actually appears.. Is it possible to solve this problem with this type of eventHandler or do I have to use some other function...

after using Page.Response to send a file to user, page doesn't respond

I've created a page that allows users to download a file when they click a button... the button's onclick event is linked to the following chunk of code:
this.Page.Response.Clear();
this.Page.Response.ContentType = System.Net.Mime.MediaTypeNames.Application.Zip;
this.Page.Response.AppendHeader("Content-Disposition", "attachment; filename=\"" + System.IO.Path.GetFileName(filename) + "\"");
this.Page.Response.TransmitFile(filename);
this.Page.Response.Flush();
this.Page.Response.End();
The download works fine, but now when I try to interact with the page, (for instance, hit the download button again), nothing posts back.
Am I responding to the download request incorrectly (should I be using a different/new response object), or is there something else I need to do to make the page active after the download?
Edit:
So I've tried encorporating the two posters suggestions for creating a httphandler, and calling a Response.Redirect to the handler from the button's click event.
void submitButton_Click(object sender, EventArgs e)
{
label.Text = "Boo!";
this.Page.Response.Redirect("~/ViewAttachment.ashx?id=foo", false);
}
If I step through this on the debugger, it continues after the redirect call, but the page just returns to a state where the button doesn't work, and the labels have their default values. Am I now doing the redirect wrong?
If using another handler an option?
Here's a slimmed down version of what I've used before (suggestions welcome this was a quick write). The HttpHandler (AttachmentFile is just a class with the blob data and some attributes collected when the file was uploaded):
public class AttachmentHandler : IHttpHandler
{
public const string QueryKeyID = "ID";
public void ProcessRequest(HttpContext context)
{
var r = context.Response;
var attachmentID = context.Request.QueryString[QueryKeyID];
Attachment a = DataContext.GetById<AttachmentFile>(attachmentID);
r.ContentType = a.ContentType;
r.AppendHeader("Content-Type", a.ContentType);
r.AppendHeader("content-disposition", string.Format("attachment; filename=\"{0}{1}\"", a.AttachmentName, GetExtension(a.FileName)));
r.BufferOutput = false; //Stream the content to the client, no need to cache entire streams in memory...
r.BinaryWrite(a.BlobData);
r.End();
}
private static string GetExtension(string fileName)
{
if(fileName.IsNullOrEmpty()) return string.Empty;
var i = fileName.LastIndexOf(".");
return i > 0 ? fileName.Substring(i) : string.Empty;
}
}
In the web.config:
<system.web>
<httpHandlers>
<add verb="*" path="ViewAttachment.ashx" type="MyNamespace.AttachmentHandler, MyWebDllName"/>
</httpHandlers>
</system.web>
Then all you do is render a link in the form of ~/ViewAttachment.ashx?ID=5 on the page, clicking the button will download the file, but not mess with your page's lifecycle at all.
Now there are more considerations, like security and such....I trimmed them out for this example. I'm also using this style of link: ~/Attachment/{id} via webforms routing...if either of these interest you I'll update to include them. Or you could hate this approach all-together...just letting you know it's an option.
You are mostly there.
All you need to do in your button click event handler is Response.Redirect to another ASPX page. Put that code your wrote on the other page.
Then everything will work just as you expect. The browser will actually stay on the original page and not go to the new page because you have set the content disposition to attachment - which is great.
By using Response.Clear() you've killed the headers. While there may appear to be valid html markup in the browser there really isn't. The headers are gone, so the controls that appear to be still rendered aren't valid. If you click "back" you should be able to repost information. What you probably want to do is make this call in a different window using PostBackUrl in your button click and setting the target of your form to a different window (then using SetTimeout to reset it to _self).

Redirecting to a "Thank you" page and offering the save dialog of downloaded file immediately

I am using ASP.NET2.0. I have created a download form with some input fields and a download button. When the download button is clicked, I want to redirect the user to a "Thank you for downloading..." page and immediately offer him/her the file to save.
I have this following code to show the savefile dialog:
public partial class ThankYouPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Clear();
Response.AddHeader("Content-Disposition",
"attachment; filename=\"downloadedFile.zip\"");
Response.ContentType = "application/x-zip-compressed";
Response.BinaryWrite(this.downloadedFileByteArray);
Response.Flush();
Response.End();
}
}
Obviously, this code does not allow to display any "Thank you" message. Is there an "AfterRender" event or something similar of the Page where, I could move this download code and give a chance for the page to render the "thank you" message to the user? After all, I am truely thankful to them, so I do want to express that.
You could reference a download page from your thank you page using an IFrame
<iframe src="DownloadFile.aspx" style="display:none;" />
In this case, DownloadFile.aspx would have the code behind from your example.
Use a META REFRESH tag in the head of your thank-you page:
<META http-equiv="refresh" content="1;URL=http://site.com/path/to/downloadedFile.zip">
Alternatively, you might use a body onLoad function to replace the current location with the download URL.
<body onLoad="document.location='http://site.com/path/to/downloadedFile.zip'">
In this case the redirection will start after the current page has finished loading and only if the client has JavaScript enabled, so remember to include a link with a download link ("If your download doesn't start in a few seconds..." and so on).
You may also use an IFRAME as suggested by Phil, or even a FRAME or a full-blown pop-up (blockable, mind you). Your mileage may vary.
The code you've written, should actually be redirected to from the 'thank you' page (making it the 2nd redirect). Because you've set the content disposition to attachment, this page will not actually replace the existing 'thank you' page.
if you want to serve a "Thank You" page and the file the client must call twice the server.
So you can just create the thankyou.aspx page for displaying the message (and maybe put a direct download link to the file).
You can start download with a meta tag or just using js (even ms do the same for their download page).
Then to serve the file you sould create a direct link to avoid another page run on the server; otherwise you should create an HttpHandler just to hide the filesys.
The file should be sent to the client with Response.TrasmitFile
Please check the following link, this may help you to sort out your issue.
In Asp.net, on button click, how to open a download dialog, and then redirect the user to another page?

Categories