Let the user download a generated file in ASP.Net - asp.net

I'm creating an ASP.Net web application and I want to add an export feature. That feature will generate a string (xml, to be specific) when a button is clicked.
I want to enable the user to download this string as a file.
Do I need to create a file on the server's hard drive and open a link to that file or is there a way to download the string directly (so ASP.net takes care of creating and removing the file)?
I am currently tryig to do the following:
Response.Clear();
Response.Buffer = true;
Response.ContentEncoding = Encoding.GetEncoding(1252);
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader("Content-Disposition", "attachment; filename=filename.txls");
//Multiple Response.Write(string) calls to add content to the file
Response.End();
Using firebug, I found out the the user receives the headers, but the file doesn't open.
The Internet Explorer is even able to show me the response text and has a button to save the file (both options are only avilable in the developer tools) and the file is correct, but nontheless, the file still doesn't open a window (nor a in-browser-preview).
When I use the Internet Explorer (which seems to have better Visual Studio debugging integration), a JavaScript error appears:
The EnableScriptGlobalization property cannot be changed during async postbacks or after the Init event.
Calls to Response.Write() seem to be a common cause of this issue. Of course, I call exactly that method multiple times.
I figured out that the JavaScript on the webpage (the script seems to be auto-generated) tries to parse the response, but of course it isn't formatted in the asp.NET internal communication format, but in my own XML format.
How can I tell the page to treat my page as a downloadable file?

string someXML=GetXMLContent();//I assume you have an implementation of this
Response.Clear();
Response.ContentType = "text/xml";
Response.AddHeader("Content-Disposition", "attachment; filename=filename.xml");
Response.Write(someXML);
Response.End();
Couple of things to note. It's probably better to build up your XML string early on, then write it to the response in one shot. It makes for cleaner code, because you can separate your concerns this way.
Also, I switched the content type to "text/xml", because this is not an Excel file. Also, your file extension was incorrect. XML by convention ends in .xml. I removed the content encoding because it's not necessary most of the time. If you're dealing with weird characters feel free to add it back. Also removed the buffer in the name of removing unnecessary things.
Try this, then try opening the resulting file in your text editor and verifying the content.

The problem was that the button responsible for triggering the Excel export was not a PostBackTrigger. As soon as I had changed it, everything worked like a charm.
Nontheless, I don't know hy this fixed everything. What does it change when I set the button as a PostBackTrigger

Related

Winform browser control "attachment" button

We have a winform app that has a browser control on it. Previously these files (always very small 10kb etc.) were stored at a unc location. We would generate some html and load the html into the browser. If we wanted to make one of these small files available we would include in the HTML an anchor tag () WHen the html was displayed in the browser control so would be the link. The user could click on the link and the file save as dialog would appear.
We are now storing these files in the db as varbinary and thus there is no longer a physical location for the anchor tag to point to. I have several thoughts but would like the members of SO who are way smarter than me to chime in.
Option 1 in my mind would be to have an image button, anchor tag, something in the html to click on. I would handle the "onclick" either in javascript or as a postback. This seems doable for my level of knowledge EXCEPT I do not know how to get the byte[] to translate into the save as dialog for the user....do I render it to disk first?
The other idea I had was to have a button that is NOT in the browser control. This button would be hidden / visible if the biz rules said to show a file. Clicking on the button would then generate the byte[] which is easily turned into a file and the save as shown dialog shown in the winform app.
So any thought or all together different suggestions welcome
TIA
JB
I understand that you are in control of the ASP.NET web page shown in the windows forms web browser control so you can edit that page and build it the way you want.
if that is true, behavior in hosted web browser or in normal IE session is the same and I would suggest to create a bunch of hyper links or buttons in the asp.net web form page each one which a specific ID, like the ID of the file to download. then you can create an handler or a button_click event handler where you get the byte[] of the file by the clicked button/link associated file Id, or from query string if you initiated an handler call, and then you start streaming down to the browser the file content, the browser will do all what is required for you.
for example, just as a starting point, a bit of code taken from here: http://social.msdn.microsoft.com/Forums/en-US/silverlightnet/thread/d6a5087f-43b1-4782-95f1-d1376130d2c8
shows you a possible way to do this from a page load, the trick is that the call to GetDocument gets the proper file content for you (in this case from the query string, imagine like if we are inside an handler processing method) and returns a class DocumentInfo which contains the bytes. you do nor need this DocumentInfo, you can just have a method which returns byte[] by File Id for example...
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
string queryString = this.Request.QueryString.ToString();
if (string.IsNullOrEmpty(queryString)) return;
DocumentInfo documentInfo = GetDocument(queryString);
if (!documentInfo.HasValue) return;
Response.ClearHeaders();
Response.ClearContent();
Response.AppendHeader("Content-Length", documentInfo.Value.Content.Length.ToString());
Response.ContentType = "application/octet-stream";
Response.AppendHeader("Content-Disposition", "attachment; filename=Test.doc");
Response.BinaryWrite(documentInfo.Value.Content);
Response.End();
}
}

PDF form submitted to ASP.NET page creates a return HTML file

I have a PDF form that has a 'Submit' button that submits its information to a ASP.NET page. The information is taken and processed into a PDF on the server for storage and later use.
This process works fine, however after the form is done submitting the Adobe viewer receives back a html 'fragment' i guess. I need to know how to format that, stop it, or otherwise handle it. I suspect the fragment is a success message but the Adobe reader doesn't seem to handle it
The HTML is
<!-- saved from url=(0014)about:internet -->
I don't have a definitive answer on how to get Acrobat to display your HTML, or if it even can, but my guess is that it won't display HTML back. I do remember having issues with this years ago, and when I had an HTTP Handler to take the form data and process it, but my final step was to redirect them code to different PDFs based on their status.
I put together different response PDF for a few different scenarios as I wasn't showing them back the document that was actually created and when the processing was done just did a context.Response.Redirect( "STATUS.pdf", false ); which Acrobat handled fine.
You could alternately try changing to ContentType to text/plain and seeing if Acrobat will display any output that way.
context.Response.ContentType = "text/plain";
context.Response.Write("Message here.");

ASP.NET Response Content

I have tested this in IIS 6.1, IE 7, ASP.NET 3.5 SP1.
I have a file download in a method in my aspx codebehind:
Response.ContentType = contentType;
Response.AppendHeader("Content-Disposition", contentDisposition);
Response.BinaryWrite(file);
This works great, but if I attempt to modify any of my sever side controls the changes do not take affect. I have isolated this down to the call to ContentType, this apparently whipes all pending changes to the Response stream when called? Does this sound familiar to anyone?
If the code takes an alternate branch and the call to download does not fire the markup is modified as expected.
Any suggestions on how I can fix this and have the page flush the attachment and update the UI in the same response stream?
This is specifically for updating the ValidationSummary, so I could tear into the JS on the PageRequestManager event complete as a last resort, but I'd prefer not to rely on JS for this.
Not sure what you're trying to do - are you trying to simultaneously serve a download file and an update to the HTML page they linked to it from? That's not how HTML works.
If you want to achieve this result then you'll basically have to render a meta redirect that goes to the file which is returned in the HTML, that way the page will load and then the download starts (Like you'll see on a lot of download sites).
As fyjham said, I don't really understand what you're trying to do. A few tips that might help:
Keep in mind that the Render phase, when the content from your markup and controls is generated, happens as almost the very last phase in your code behind (well after Page_Load)
Once you flush headers, you can't set them again
Controls can override some HTTP headers
You can't mix a file download and HTML markup in the same HTTP response

How can I send a file AND the page in an HTTP response?

I have an ASP.NET page where I'm generating an Excel spreadsheet and sending that in the response (content-disposition header). That part works great, but I have a button that never becomes re-enabled because the original page isn't part of the response.
Here's the function I'm using:
Public Shared Sub WriteToResponse(ByVal theWorkBook As Workbook, ByVal FileName As String, ByVal resp As HttpResponse)
Dim theStream As New System.IO.MemoryStream()
theWorkBook.Save(theStream)
Dim byteArr As Byte() = DirectCast(Array.CreateInstance(GetType(Byte), theStream.Length), Byte())
theStream.Position = 0
theStream.Read(byteArr, 0, CInt(theStream.Length))
theStream.Close()
resp.Clear()
resp.AddHeader("content-disposition", "attachment; filename=" & FileName)
resp.BinaryWrite(byteArr)
resp.End()
End Sub
I tried not clearing the response and using AppendHeader instead of AddHeader, but the response still contains only the file.
What am I missing? Do I need to use a different header? I found this unanswered question on the ASP.NET forums; sounds like my problem.
You can't. You can fake the behaviour by opening a popup on clicking the button and have the popup serve up the file and posting back the page from the javascript. But in this manner you are effectively creating 2 requests with 2 responses.
update: if you want to disable the button when they start the download and enable it as soon as the download starts you can't really do this but you could use some trickery to make it appear like that. When they click on the download button use some javascript to disable the button and to create the popup that kicks off the file streaming. When the filestreaming function (WriteToResponse) returns set some value in the users session. In the mean time have the original page poll the server using AJAX to see if the session value is set. If it's set re-enable the button and set to the value to empty or false again (so this works a 2nd time). But this seems like a whole lot of tricks.
but I think in this case the cure is worse then the problem. I assume you want this because generating the file takes a while and you don't want users to reset the flow by pressing the button impatiently while the file is being prepared. To accomplish this I would just show a popup message in the page on button press saying something like "Your download is being prepared. This might take a minute. Thank you for your patience. (click to dismiss this message)"
You cannot send back a file and a new page in the same response.
I'm not sure how the solution fits your particular needs, but remember that you can use Javascript to enable or disable a button right before the file-creating request is sent to the server (so that, by the time spreadsheet download begins for the user, the button is already in the state you want it in).

ASP.Net HyperLink and Download directly?

want to have a Hyperlink-Button in a gridView in which I can display a Link for a direct download of files.
So far I am okay, but when I link the file in the NavigateURL, I don't get the file via click, I must right click -> Save as!
That I don't want. Any help?
You could set up an ashx file handler. Your ashx takes the request through the querystring, loads the proper file into memory, sets the proper response headers and streams the file to the browser:
FileInfo fileInfo = new FileInfo(PATH-TO-YOUR-FILE); //You need to specify this
context.Response.ContentType = YOUR-CONTENT-TYPE; //And this
context.Response.AddHeader("Content-Length", fileInfo.Length.ToString());
context.Response.WriteFile(fileInfo.FullName);
context.Response.Flush();
context.ApplicationInstance.CompleteRequest()
This lets you have some fine-grain control over the content that is being delivered if you have any concerns about security or maybe keeping track of who has downloaded what file, etc.
This sounds like a browser issue. Possibly the browser is trying to open up some application to handle this and failing. Double-check your application associations and/or try a new browser.
I would add also "Content-Disposition" header to response:
context.Response.AppendHeader("Content-Disposition", "attachment; filename = " + filename);

Resources