Trouble cleaning up after serving generated file from asp.net Page_Load - c#

I want to serve a dynamically created binary file from an asp.net web page via a http request parameter like so: www.host.com/?Download=file. So far I have
protected void Page_Load(object sender, EventArgs e)
{
if (Request.Params.Allkeys.Contains("Download"))
{
String fileStr = Request.Params.GetValues("Download")[0];
using (Stream generatedFile = File.Create(#"C:\Temp\file"))
{
/* write the contents of the file */
}
Response.ContentType = "application";
Response.AddHeader("Content-Disposition",
"attachment; filename=" + fileStr);
Response.WriteFile(#"C:\Temp\file");
Response.End();
}
}
which works, but C:\Temp\file still exists and I'd like it cleaned up. Calling File.Delete(#"C:\Temp\file"); after Response.End() doesn't work, since Response.End() kills the thread. Putting the Response methods inside the using block, with File.Create( (...), FileOptions.DeleteOnClose) doesn't work, since the Response methods then fail to get access to the file.
Any suggestions?

After writing file to response you can call Response.Flush to make sure it was fully written, and then delete it before calling Response.End:
Response.WriteFile(#"C:\Temp\file");
Response.Flush();
File.Delete(#"C:\Temp\file");
Response.End();

Related

Download a Large file Async in ASP.NET C#

I have the code below which works well for small files but for large files it generates the zip as required but doesn't download it. I get all sorts of errors including Timeout (which I have managed to resolve). The other problem is that it runs in Sync. The largest file I have generated myself is a 330MB zip file with about 30 HD images attached to it. But this can even go to GBs as the user can choose to download about 100 or even more HD images at once.
To resolve both issues, I thought downloading in async may help in both cases. I want to alert the user that their download has started, and that they will be notified when it is ready.
I am thinking of sending the stream down if the client IsConnected (then delete the file) or sending an email to ask them to download the file if they have decided to logout (then delete the file using the offline download link). I just don't know where or how to write async code, or if what I want to do can actually be done if the user decides to logout.
Here's my current code:
private void DownloadFile(string filePath)
{
FileInfo myfile = new FileInfo(filePath);
// Checking if file exists
if (myfile.Exists)
{
// Clear the content of the response
Response.ClearContent();
// Add the file name and attachment, which will force the open/cancel/save dialog box to show, to the header
Response.AddHeader("Content-Disposition", "attachment; filename=" + myfile.Name);
// Add the file size into the response header
Response.AddHeader("Content-Length", myfile.Length.ToString());
// Set the ContentType
Response.ContentType = "application/octet-stream";
Response.TransmitFile(filePath);
Response.Flush();
try
{
myfile.Delete();
}
catch { }
}
}
I don't know about Async downloads from asp.net applications so I can't address that question. But I have run into enough download issues to always start from the same place.
First, download from a generic handle (ASHX) and not a web form. The webform wants to do extra processing at the end of the request that can cause problems. You question didn't state if you are using a web form or generic handler.
Second, always end the request with the ApplicationInstance.CompleteRequest() method call. Don't use Request.Close() or Request.End()
Those two changes have often cleaned up download issues for me. Try these change and see if you get the same results. Even if you do get the same results this is a better way of coding downloads.
Finally, as an aside, only catch appropriate exceptions in the try-catch bock.
Your code would be like this:
public class Handler1 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// set from QueryString
string filePath = "...";
FileInfo myfile = new FileInfo(filePath);
// Checking if file exists
if (myfile.Exists)
{
// Clear the content of the response
context.Response.ClearContent();
// Add the file name and attachment, which will force the open/cancel/save dialog box to show, to the header
context.Response.AddHeader("Content-Disposition", "attachment; filename=" + myfile.Name);
// Add the file size into the response header
context.Response.AddHeader("Content-Length", myfile.Length.ToString());
// Set the ContentType
context.Response.ContentType = "application/octet-stream";
context.Response.TransmitFile(filePath);
context.Response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest();
try
{
myfile.Delete();
}
catch (IOException)
{ }
}
}
public bool IsReusable
{
get
{
return false;
}
}
}

Exception generated by Response.End()

I am letting user download file using following code. It works fine but also generates error and source for error is Response.End();
Error Message :Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack.`
Below is my code. How can i handle this error and is this error not freeing my resources which can result in unwanted memory use.
I use this for asp.net webform application.
try
{
string fileName = Request["file_ID"];
string path = Server.MapPath("~/App_Data/uploads/"+fileName);
//string = Server.MapPath(strRequest);
System.IO.FileInfo file = new System.IO.FileInfo(path);
if (file.Exists)
{
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=" + file.Name);
Response.AddHeader("Content-Length", file.Length.ToString());
Response.ContentType = "application/"+file.Extension;
Response.WriteFile(file.FullName);
Response.End();
// System.Threading.Thread.Sleep(2000);
// Page.ClientScript.RegisterStartupScript(this.GetType(), "MyScript", "alert('aa')", true);
}
else
{
//Page.ClientScript.RegisterStartupScript(this.GetType(), "MyScript", "myWindow.close();", true);
}
}
catch (Exception rt)
{
Response.Write(rt.Message);
}
}
I was looking at this solution but i am not sure how i can implement it in my code.
Unable to evaluate expression... on web page
UPDATE:
I actually want user to download file and close same with using code behind script which are commented in code right now.
So i am not sure how to better optimize this code to handle the exception generated by respond.end statement.
I tried to use Page.ClientScript.RegisterStartupScript(this.GetType(), "MyScript", "myWindow.close();", true); but it doesn't work either.
Thanks
The other answer seems to be saying you can either not use Response.End as it is unnecessary, or add a catch for ThreadAbortException where you have your current catch
The question links to another question that suggests adding the following:
// Sends the response buffer
Response.Flush()
// Prevents any other content from being sent to the browser
Response.SuppressContent = TrueResponse.SuppressContent = True
use HttpContext.Current.ApplicationInstance.CompleteRequest
OR TRY SOMETHING LIKE
Response.OutputStream.Flush()
Response.OutputStream.Close()
Response.Flush()
Response.End()
READ Is Response.End() considered harmful?

how to make code execute after Response.end

My code is like this
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ContentType = "application/pdf";
HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename=" + "name" + ".pdf");
HttpContext.Current.Response.TransmitFile("~/media/pdf/name.pdf");
HttpContext.Current.Response.End();
if (FileExists("/media/pdf/name.pdf"))
{
System.IO.File.Delete("D:/Projects/09-05-2013/httpdocs/media/pdf/name.pdf");
}
Here I want to download name.pdf in the browser, and after the download I want o delete that file.But the code execution stops at
HttpContext.Current.Response.End();
no code after that line is executed.so my delete function is not working.Is there any work around for this issue?
// Add headers for a csv file or whatever
Response.ContentType = "text/csv"
Response.AddHeader("Content-Disposition", "attachment;filename=report.csv")
Response.AddHeader("Pragma", "no-cache")
Response.AddHeader("Cache-Control", "no-cache")
// Write the data as binary from a unicode string
Dim buffer As Byte()
buffer = System.Text.Encoding.Unicode.GetBytes(csv)
Response.BinaryWrite(buffer)
// Sends the response buffer
Response.Flush()
// Prevents any other content from being sent to the browser
Response.SuppressContent = True
// Directs the thread to finish, bypassing additional processing
HttpContext.Current.ApplicationInstance.CompleteRequest()
HttpResponse.End (as per documentation) raises a ThreadAbortException and as you do no attempt to handle this your method exits.
I'm not sure exactly why you must use End(), but you could put the "cleanup" code in a finally statement.
Maybe fire some async method (fire and forget style) to delete the file or have a clean-up service on the server to delete all your files after certain time and rule.
Like mentioned about Reponse.End is pretty harsh and final... more details here:
Is Response.End() considered harmful?
just my thoughts on that... =)
I had the same issue.
try this: copy to MemoryStream -> delete file -> download.
string absolutePath = "~/your path";
try {
//copy to MemoryStream
MemoryStream ms = new MemoryStream();
using (FileStream fs = File.OpenRead(Server.MapPath(absolutePath)))
{
fs.CopyTo(ms);
}
//Delete file
if(File.Exists(Server.MapPath(absolutePath)))
File.Delete(Server.MapPath(absolutePath))
//Download file
Response.Clear()
Response.ContentType = "image/jpg";
Response.AddHeader("Content-Disposition", "attachment;filename=\"" + absolutePath + "\"");
Response.BinaryWrite(ms.ToArray())
}
catch {}
Response.End();

Force download ASP.Net

In ASP.Net (with C#) I'm trying to create a .DAT file with plain text in it and send it to the browser and force download. I've tried several things but I can't get it working. In my aspx-file there is an ImageButton
<asp:ImageButton ID="btnSave" runat="server" CausesValidation="False" ImageUrl="~/Images/Stages/Database/Save.png" OnClick="btnSave_OnClick" Width="26px" />
In the OnClick-method I'm trying to create the file and send it to the browser.
protected void btnSave_OnClick(object sender, EventArgs e)
{
string file = "test.dat";
string fileName = "~\\Stages\\Broekx\\Databanken\\" + file;
FileStream fs = new FileStream(MapPath(fileName), FileMode.Open);
long cntBytes = new FileInfo(MapPath(fileName)).Length;
byte[] byteArray = new byte[Convert.ToInt32(cntBytes)];
fs.Read(byteArray, 0, Convert.ToInt32(cntBytes));
fs.Close();
ImageButton btnSave = (ImageButton)FormViewStagesDummy.FindControl("btnSave");
btnSave.Visible = false;
File.Delete(Server.MapPath(fileName));
if (byteArray != null)
{
this.Response.Clear();
this.Response.ContentType = "text/plain";
this.Response.AddHeader("content-disposition", "attachment;filename=" + file);
this.Response.BinaryWrite(byteArray);
this.Response.End();
this.Response.Flush();
this.Response.Close();
}
}
The file test.dat exists in the correct folder and has to be deleted after it has been read into bytes. I've tried this without deleting the file and that wont work either.
After clicking btnSave the button has to be hidden, so that's why I set the parameter Visible to false.
I've also tried it with content-type "application/octet-stream" or with a PDF file and content-type "application/pdf" but nothing works. The page loads normally and no file is being downloaded.
Is the file string's path actually correct?
this.Response.AddHeader("content-disposition", "attachment;filename=" + file);
Should it not be filename?
Why are you deleting the file before it is written to the response? Would it not make more sense to serve the file via the response and then delete it?
i.e. call
File.Delete(Server.MapPath(fileName));
after the repsonse.
You should try:
Response.TransmitFile( Server.MapPath(fileName) );
Response.End();
TransmitFile is very efficient because it basically offloads the file streaming to IIS including potentially causing the file to get cached in the Kernal cache (based on IIS's caching rules).
Response.End();
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "text/plain";
Response.AppendHeader("Content-Disposition", "attachment; filename = " + fileName);
Response.TransmitFile(Server.MapPath("~/foldername/" + fileName));
Response.End();

providing dynamic file download in ASP.NET2.0

I want to provide dynamic download of files. These files can be generated on-the-fly on serverside so they are represented as byte[] and do not exist on disk. I want the user to fill in an ASP.NET form, hit the download button and return the file he/she wanted.
Here is how my code behind the ASP.NET form looks like:
public partial class DownloadService : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void submitButtonClick(object sender, EventArgs e)
{
if (EverythingIsOK())
{
byte[] binary = GenerateZipFile();
Response.Clear();
Response.ContentType = "application/zip";
Response.ContentEncoding = System.Text.Encoding.UTF8;
Response.BinaryWrite(binary);
Response.End();
}
}
...
}
I expected this piece of code just work. I clear the Respone, put in my generated zip file and bingo. However, this is not the case. I get the following message in the browser:
The XML page cannot be displayed
Cannot view XML input using style sheet. Please correct the error and then click the Refresh button, or try again later.
An invalid character was found in text content. Error processing resource 'http://localhost:15900/mywebsite/DownloadS...
What am I doing wrong?
Here's a minor modification you need to make:
Response.Clear();
Response.ContentType = "application/x-zip-compressed";
Response.BinaryWrite(binary);
Response.End();
This is my (working) implementation:
Response.Clear();
Response.ContentType = mimeType;
Response.AddHeader("Content-Disposition", String.Format("attachment; filename=\"{0} {1} Report for Week {2}.pdf\"", ddlClient.SelectedItem.Text, ddlCollectionsDirects.SelectedItem.Text, ddlWeek.SelectedValue));
Response.BinaryWrite(bytes);
Response.Flush();
Response.End();
mimeType is like your application/zip (except PDF).
The main differences are the extra header information passed, and the Flush call on the Response object.
Look here for informationa about application/zip . It might be that the ContentEncoding is wrong. And here's a guide on sending other types. Also here is a guide on how to do it for pdfs.

Categories