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();
}
}
Related
Main purpose here is to target the handler to download a file that is being previewed.
There are two different conditions of the file, one where it is already saved in the database, then the following parameters to the handler through a query string
COIHandler.axd?Action=preview_saved&letterId=xxxx
or
one that hasn't been saved yet, in which i store it in the session and target the handler in the following way
COIHandler.axd?Action=preview_unsaved
then the handler will handle clearing the session after I'm done.
Any thoughts on how to make this occur. Still new to http handlers.
CLARIFICATION
I have a OnClientClick property in my sub classed version of the Button class which allows me to append javascript to it that will be executed client-side. That is where I want the targeting to occur.
I want the user stay on the same page when they click the download button as well.
I dont want to simply do a redirect in javascript that will target the handler and I would like to avoid a pop-up window if possible. Not really sure if that leaves any options
In Response to Yads comment, you can use a button click handler on your page to download the file without a need for a popup or separate HTTP Handler.
protected void btnDownload_OnClick(object sender, EventArgs args)
{
PDFContentData data = GeneratePDFData(); //parses strings from some source (as mentioned in comments)
WebFileUtil.InvokePDFDownload(Page.Context, data);
}
public static class WebFileUtil
{
public static void InvokePDFDownload(HttpContext context, PDFContentData data)
{
context.Response.Clear();
context.Response.ClearContent();
context.Response.ClearHeaders();
FileStore.generateDocument(data, context.Response.OutputStream);
context.Response.ContentType = "application/pdf";
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
context.Response.AddHeader("Content-Disposition", String.Format("attachment;filename={0}_Documenth.pdf", DateTime.Now.ToShortDateString()));
context.Response.End();
}
}
Your handler would actually be .ashx. Other than that your approach seems fine. You can examine the context that is passed into the PrcoessRequest method.
public void ProcessRequest(HttpContext context)
{
switch(context.Request.QueryString["Action"].ToLower())
{
case "preview_saved":
//do stuff for saved
case "preview_unsaved":
//do stuff for unsaved
}
}
UPDATE If you want a central handler, then you'll either want to redirect the user at the end of the call in which case you'll also want to pass a returnUrl parameter in the query string. Then you can just do a context.Response.Redirect(context.Request.QueryString["returnUrl"]). Or if you want the page to retain its state, you're going to have to make an Ajax call to your handler.
First off thank you very much for all the possible solutions they were helpful in getting outside the box.
I figured out a unique solution that I thought I would share.
I place an invisible iframe in the html markup of my page.
then I set a property called OnClientClick in my server control Download Button (this allows me to render a parameter string to the onclick attribute of the html)
I set the property to a javascript method which embodies a recursive algorithm to navigate to the top parent frame of the page, once it reaches that point, I use a simply JQuery selector to look for the iframe I spoke of in step 1. (The reason for the recursion is to maintain support with Master Pages where you will have frames inside of frames).
Set the src attribute of the iframe to the target HTTP Handler. The applied behavior causes the iframe to load the requested source ... but in this case it is a handler, and since it was made all originally from direct user input (via the Download button click), you won't even have the issue of getting the Yellow Bar with IE7+
The handler serves the request and the file is served to the browser
Yay!
In my ASP.NET MVC2 app, I want to have a button in my view that collects some parameters off some form fields, then calls a controller action that returns a FileResult to stream a file (according to the parameters) to a new brower window.
I'm fine with using jquery AJAX to call the controller action, passing the paramters, but how do I get a new window to open for the streamed file?
EDIT: I know how to do stream the file back to the client browser - I'm just asking about how to write the View to collect some parameters to pass to the action.
For example, I have a fairly complex form, divided up into tabs using jquery.tabs(). On one of the tabs, the user can enter a dollar amount into a textbox, choose a template from the drop-down list then click a button to generate a voucher. So, I need to collect the values off the dollar amount box and drop-down selection (and I want to validate these, client-side) then call the controller action to generate a file to stream back to the browser.
So, my queestion is mainly a noob MVC question - what do I write in the view to specify a "mini form" that contains just those two fields, then validate them (I think I'm OK with that) then pass them to a controller action? (And I'm fine with using the link and specifying the target as _blank to open a new window.)
You usually don't call controller actions returning files using AJAX because you won't be able to do much with this file in the success callback (you cannot store the file to the client computer). So there are two possibilities: either the user is prompted with a dialog box to choose the location he wants to save the file or the contents of the file is opened inline with the default associated program (if there is any). It is the Content-Disposition header which is used to control this behavior:
Control-Disposition: attachment;filename=foo.pdf
will show a Save As dialog box to the user and it will stay on the same page. There won't be any redirect happening.
Control-Disposition: inline;filename=foo.pdf
will try to open the contents of the file inside the browser if there is an associated program and it will replace the current page.
If you want to open the contents of the file in a new browser window you could use target="_blank":
Download
Obviously this is only necessary if you are opening the file inline. For files that should be saved to the user computer you should simply provide the link and leave the user decide what to do with the file. No AJAX.
If I understand well, you must create a MemoryStream object
MemoryStream m = new MemoryStream();
Fill the object and then
HttpContext.Current.Response.Buffer = true;
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.ContentType = "application/ms-word";
HttpContext.Current.Response.AddHeader("Content-Disposition","attachment;filename=document.doc");
HttpContext.Current.Response.BinaryWrite(m.GetBuffer());
HttpContext.Current.Response.End();
Here the list of the mime-type allowed
http://msdn.microsoft.com/en-us/library/ms775147%28VS.85%29.aspx
I have an ASP.NET page (using ASP.NET AJAX and the Telerik RAD components) that provides a data table and an option to download data in CSV format.
I have a requirement that a single button allow the user to download a data set. I have a system set up that's nearly working for me based off the solution in
Response.Redirect to new window
My ASPX looks like this:
<asp:Button ID="ExportCsvButton" runat="server" Text="Download to CSV/Excel"
OnClick="ExportCsvButton_Clicked" OnClientClick="aspnetForm.target = '_blank';" />
Then in my codebehind for ExportCsvButton_Clicked, I do something like this:
byte[] csvBytes = someLongOperation();
Session[EXPORTED_SESSION_KEY] = csvBytes;
Response.Redirect("myexportpage.aspx", true);
Finally, in my 'myexportpage' codebehind, I'm doing this:
protected void Page_Load(object sender, EventArgs e)
{
Response.Clear();
Response.ClearHeaders();
Response.ClearContent();
Response.AppendHeader("content-disposition", "attachment; filename=" + "file.csv");
Response.ContentType = "text/csv";
Response.AddHeader("Pragma", "public");
Response.BinaryWrite(Session[OtherPage.EXPORTED_SESSION_KEY] as byte[]);
Response.Flush();
Response.End();
}
This actually works quite well. When I click the 'Export to CSV' button, the page's UpdateProgress kicks in and my loading overlay appears (it's a CSS div that covers the window when visible). This can take some time depending on the data set (potentially several minutes), at which point I'm getting a window popup with my file. I can save or open the file in my browser and when I save the file I can continue on the page with my export button. Cool.
My only problem is that the UpdateProgress never goes away - the postback isn't completing (because I'm redirecting the response to the mini export page and there's no dedicated response to the postback request?).
I can use a very brittle solution, if need be, as this functionality is unlikely to be needed anywhere else in the web app for the remainder of its life.
I'm very green at AJAX so wouldn't be surprised if there isn't already an elegant solution I can use. What I really want to avoid is forcing the user to initiate a second request to initiate the download (like you see on file download sites = "your download won't begin in the next 5 seconds, so just click here").
Any thoughts?
I don't think this is possible - the file download and the result to the UpdateProgress need two separate responses.
My solution required 2 clicks but no page transitions.
My Export button fired off the action to generate the exported data, which I serialized in the user's session. When this action completed, I hid the export button and made a 'Your download it ready. Click here to save' link visible in its place on my page. That all happened at the end of the event handler for the export button.
When the user clicked the download link, the event handler for that sent the export data to the user.
I opted to leave the download link visible after that until the user refreshed the page or changed some other controls on the page that would change what data was exportable.
I have an ASP.NET2.0 web page with a Submit button.
When the user clicks, I generate an XML file on the fly and return that as a result.
Here is the code:
protected void submitBtn_Click(object sender, EventArgs e)
{
string result = this.ProduceMyXmlResult();
this.Response.Clear();
this.Response.StatusCode = 200;
this.Response.ContentType = "application/xml";
this.Response.ContentEncoding = System.Text.Encoding.UTF8;
this.Response.Write(result);
this.Response.End();
}
The piece of code does exactly what I want. However, the browser does not recognize the XML file as a new page, so the BACK button does not take me back to my original page. Why and how can I overcome that?
The simplest way to do so, I think, would be to create a separate page that executes this code on Page_Load(), and redirect to it when the button is pressed.
The reason you have no backward navigation is because the browser is unaware the page has changed. Since the Submit button is preforming a postback, and you are returning XML data as the response to that postback, it appears to the browser as though this is just some transformation of the current page (just as though you'd, say, changed the text of a Label control).
The "correct" way to accomplish this would be with some type of HTTP handler, but I haven't the experience to suggest the proper way to do so and you already have working C# code-behind for this method.
I am trying to place an action to happen after an entire .aspx page displays. The "action" is a function that uses the Response object to send a file to the user.
More detailed information:
I am trying to replicate the behavior of a link on the page, from a sidebar. I.E. I have a link on the main page for the Export action, and it works fine -- since the page is already displayed before the user clicks it. But when the user is on a sidebar, clicks the link, it should take them back to this main page and then send the file after it displays.
I did some research and thought that using the PageComplete event would be well-suited for this, so I created my event handler and put the call to the export code (it keys off of a query string when loaded from the sidebar) inside my PageComplete event handler. But it behaves just the same way - the browser download box pops up and the page is never loaded before or after.
If it helps to understand what I'm doing here is a snippet of the code used to send the list to the user.
Response.Clear();
Response.BufferOutput = true;
Response.ContentType = "application/ms-excel";
Response.AppendHeader("content-disposition", "attachment;filename=MyList.xls");
Response.Write(listManager.ExportLists(mycode));
Response.End();
I would prefer a way to use a page event to load the page, rather than tinkering with this logic. But, if there is a clean and easier way to get the file sent, and it allows for loading the page then sending the file, that would be fine too.
Is there another Page event I can use besides PageComplete, or is it possible I am missing something?
EDIT: Sorry about the verbosity. I realize that I can't change the way HTTP requests work - I'm only looking for an acceptable solution that achieves more or less the same results. It seems like the way to go is to force a refresh of the page after a couple of seconds (thus ensuring that it loads before the file download code is executed) -- so I am looking for a way to do this as the first answer suggests - refresh to a download. (It doesn't have to be delayed either, if there's a way to refresh with no waiting)
Why doesn't this code work?
private void Page_LoadComplete(object sender, System.EventArgs e)
{
if (Request.QueryString["action"] != null)
{
if (Request.QueryString["action"] == "export")
{
Response.Redirect("MyHome.aspx?action=exportnow", false);
}
if (Request.QueryString["action"] == "exportnow")
{
ExportMasterList();
}
}
}
What it's supposed to do: after page loading is complete, do a Response.Redirect, reloading itself with a different query string. When it reaches the Page LoadComplete event again, the second time it will trigger the function which writes out the file.
What it actually does: Apparently repeats the same problem twice... it goes back to the same problem, how do you execute an action after the page loads, or wait until the page completely finishes loading, then trigger a refresh which will execute the action? Is there no way for ASP.NET to do something by itself without the user clicking on something?
If that's the case, then an auto-refresh after 2 seconds would also be acceptable... but I'm not sure how to do that.
The server can only return one object to the user, a file download or a page. The best you can manage is to return the user to a page that refreshes to a file download.