How to download, then delete file in ASP.NET properly? - asp.net

I've stumbled upon this problem recently: In my app, I'm providing users with the option of downloading multiple files from a list by putting them into a .zip folder and then downloading that. I naturally want that .zip folder to be deleted once the download is finished. Here's my approach:
try {
Response.Clear();
Response.ContentType = "application/zip";
Response.AddHeader("Content-Disposition", "attachment; filename=filename.zip");
Response.TransmitFile(archive);
Response.Flush();
success = success && true;
return success;
} catch {
return false;
} finally {
System.IO.File.Delete(archive);
Response.End();
}
Now, the booleans are just to indicate whether or not was the download successful and I don't think they are important right now.
I thought how this works is the program first tries to transmit the file to the client, if no errors occur it skips the catch block and only after it was downloaded will it delete the archive file.
However, I am often getting an error saying The process cannot access the file because it is being used by another process. on the line System.IO.File.Delete(archive);. This doesn't happen every time, as far as I can see, it's rather random.
Could anyone hint me at the solution?

Response.ContentType = ContentType;
Response.AppendHeader("Content-Disposition",
"attachment; filename=myFile.txt");
Response.WriteFile(Server.MapPath("~/uploads/myFile.txt"));
Response.Flush();
System.IO.File.Delete(Server.MapPath("~/uploads/myFile.txt"));
Response.End();

Related

Getting a corrupted XLSX file when writing it to Response.OutputStream

In ASP.Net, I'm using NPOI to write save to an Excel doc and I've just moved to version 2+. It worked fine writing to xls but switching to xlsx is a little more challenging. My new and improved code is adding lots of NUL characters to the output file.
The result is that Excel complains that there is "a problem with some content" and do I want them to try to recover?
Here is a pic of the xlsx file that was created from my Hex editor:
BadXlsxHexImage Those 00s go on for several pages. I literally deleted those in the editor until the file opened without an error.
Why does this code add so many NULs to this file??
using (var exportData = new MemoryStream())
{
workbook.Write(exportData);
byte[] buf = exportData.GetBuffer();
string saveAsFileName = sFileName + ".xlsx";
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}; size={1}", saveAsFileName, buf.Length.ToString()));
Response.Clear();
Response.OutputStream.Write(buf, 0, buf.Length);
exportData.Close();
Response.BufferOutput = true;
Response.Flush();
Response.Close();
}
(I've already tried BinaryWrite in place of OutputStream.Write, Response.End in place of Response.Close, and setting Content-Length with the length of the buffer. Also, none of this was an issue writing to xls.)
The reason you are getting a bunch of null bytes is because you are using GetBuffer on the MemoryStream. This will return the entire allocated internal buffer array, which will include unused bytes that are beyond the end of the data if the buffer is not completely full. If you want to get just the data in the buffer (which you definitely do), then you should use ToArray instead.
That being said, why are you writing to a MemoryStream at all? You already have a stream to write to: the OutputStream. Just write the workbook directly to that.
Try it like this:
Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", saveAsFileName));
workbook.Write(Response.OutputStream);
Response.Flush();
Response.Close();

Chrome showing "canceled" on successful file download (200 status)

Not sure if this is an actual problem or not, but I'm writing a file out in ASP.NET, and even though the file always successfully goes through, in Chrome's developer tools, network tab, I always see the line in red, marked "Canceled".
I've tried lots of ways of doing this - for simplicity, I'm trying this with a simple text file, but it's true for PDF and other file types as well.
WebForms: I've tried it with lots of combinations of the following:
Response.Clear();
// and/or/neither
Response.ClearHeaders();
// with and without this
Response.Buffer = true;
Response.Charset = "";
// or/neither
Response.Charset = "utf-8";
// application/pdf for PDF, also tried application/octet-stream
Response.ContentType = "text/plain";
// with and without this
Response.AddHeader("Content-Length", bytes.Length.ToString());
Response.AddHeader("Content-Disposition", "attachment; filename=1.txt");
// bytes is the UTF8 bytes for a string or the PDF contents
new MemoryStream(bytes).WriteTo(Response.OutputStream);
// or
Response.Write("12345");
// any combination of the following 3, or none at all
Response.Flush();
Response.Close();
Response.End();
MVC (2 and 3, haven't tried 4):
byte[] fileContents = Encoding.UTF8.GetBytes("12345");
return File(fileContents, "text/plain", "1.txt");
// or
return File(#"C:\temp\1.txt", "text/plain", "1.txt");
It's always the same - the file goes through just fine, but dev tools shows me this:
I'm thinking of just ignoring it and moving on with life, but the red just bothers me. Any idea how I can deal with that?
This just means that chrome didn't navigate away from the page. The behavior is by design. Don't worry about it.

ASP.Net Transmit File

I am writing a webapplication in ASP.net.
I am trying to make a file dialog box appear for downloading something off the server.
I have the appropriate file data stored in a variable called file.
File has fields:
FileType - The MIMEType of the file
FilePath - The server-side file path
Here's the code so far:
Response.Clear();
Response.ContentType = file.FileType;
Response.AppendHeader("Content-Disposition", "attachment; filename=" + GetFileName(file));
Response.TransmitFile(file.FilePath) ;
Response.End();
GetFileName is a function that gets me the filename from an attachment object. I only store the path.
The above code is in a function called "Download_Clicked" that is an event that triggers on click. The event is mapped to a LinkButton.
The problem is that when I run the above code, nothing happens. The standard dialog box does not appear.
I have attempted the standard trouble-shooting such as making sure the file exists, and ensuring the path is correct. They are both dead on the mark.
My guess is that because my machine is also the server, it may not be processing properly somehow.
Thanks in advance.
Edit 1: Attempted putting control onto another page, works fine.
Edit 2: Resolved issue by removing control from AJAX Update Panel.
I've found another to do this without removing the update panel. Place the code below in your page load and you'll now be able to use that button to trigger a download.
ScriptManager.GetCurrent(this.Page).RegisterPostBackControl(Button);
Use Response.WriteFile() instead.
Also, don't use Response.End()! This aborts the thread. Use Response.Flush(); Response.Close();
Try changing
Response.AppendHeader("Content-Disposition", "attachment; filename=" + GetFileName(file));
To
Response.AppendHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(GetFileName(file))));
If that doesn't work, you can always use Response.BinaryWrite or Resonse.Write to stream the file to the web browser
Here is how transmit the file using Response.Write or Response.BinaryWrite. Put these functions in a library somewhere then call them as needed
public void SendFileToBrowser(String FileName, String MIMEType, String FileData)
{
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=" + FileName);
Response.ContentType = MIMEType;
Response.Buffer = true;
Response.Write(FileData);
Response.End();
}
public void SendFileToBrowser(String FileName, String MIMEType, Byte[] FileData)
{
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=" + FileName);
Response.ContentType = MIMEType;
Response.Buffer = true;
Response.BinaryWrite(FileData);
Response.End();
}
Then somewhere you call these functions like so
SendFileToBrowser("FileName.txt", "text/plain", "Don't try this from an Update Panel. MSAjax does not like it when you mess with the response stream.");
See edit on initial post.
Removed Ajax Update Panel to resolve the error. The panel was stopping the post back to the server.
For more info, see Cris Valenzuela's comment.

How can I send a download of a file from a modal window?

Currently, this code works fine in a regular browser window:
if (readerObj.Read())
{
filename = readerObj["TRANATTACHMENTNAME"].ToString();
fileBytes = (byte[])readerObj["TRANATTACHMENT"];
Response.Clear();
Response.ContentType="application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
Response.BinaryWrite(fileBytes);
Response.Flush();
Response.End();
dbConnectorObj.Connection.Close();
dbConnectorObj = null;
return true;
}
Unfortunately, this window needs to be modal (i'm modifying an already existing application). When I run the window modally, there's no file download dialogue.
ASP.NET 2.0
Any thoughts?
I would change the way you are doing this and have the file be server via an HTTP handler. Then you can just link the the handle url passing in the pertinent data to pull correct file or perform authentication and the dialog will pop up regardless.

Zipping Files and Downloading them Using SaveAs Dialog

I have the following code to zip all the files and then save it to the harddisk. I want zip all the files (this is done) and then attach the zip file to the Response stream so that the user have the option to save it!
protected void DownloadSelectedFiles_Click(object sender, EventArgs e)
{
string path = String.Empty;
string zipFilePath = #"C:\MyZipFile.zip";
ZipOutputStream zipStream = new ZipOutputStream(File.Create(zipFilePath));
byte[] buffer = new byte[4096];
foreach (GridViewRow row in gvFiles.Rows)
{
bool isSelected = (row.FindControl("chkSelect") as CheckBox).Checked;
if (isSelected)
{
path = (row.FindControl("lblUrl") as Label).Text;
ZipEntry entry = new ZipEntry(Path.GetFileName(path));
entry.DateTime = DateTime.Now;
zipStream.PutNextEntry(entry);
using (FileStream fs = File.OpenRead(path))
{
int sourceBytes;
do
{
sourceBytes = fs.Read(buffer, 0, buffer.Length);
zipStream.Write(buffer, 0, sourceBytes);
} while (sourceBytes > 0);
}
}
}
zipStream.Finish();
zipStream.Close();
Response.ContentType = "application/x-zip-compressed";
Response.AppendHeader("Content-Disposition", "attachment; filename=SS.zip");
Response.WriteFile(zipFilePath);
Response.End();
}
I blogged about sending files like this a while ago. You might find something usefull in there.
http://absolutecobblers.blogspot.com/2008/02/downloading-and-deleting-temporary.html
If you are using IE, check that its not the old "cache is full"-bug that is showing its ugly face.
And if you have IE set to refresh cashe Automatically or on IE-start and have downloaded that zip-file broken the first time, then it could be that it uses the cached version of that zip-file after you fixed your routine and got a good zip.
Try to add:
Response.Buffer = true;
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
before your Response.ContentType
and add this:
Response.Flush()
Response.Close()
before response.end, and see if that changes anything.
So the result is this:
Response.Buffer = true;
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/x-zip-compressed";
Response.AppendHeader("Content-Disposition", "attachment; filename=SS.zip");
Response.WriteFile(zipFilePath);
Response.Flush()
Response.Close()
Response.End();
Just some tips just from the top of the mind.
I also tried to save the zip file to the server folder and then giving a link to the user to download it but it says the same "The file is corrupted". This is very strange since I can open the same folder if I visit my server folder and manually open it!
Here is another thing I found. If I save the zip file in the Server's folder and then reference it using the url: http://localhost/MyApplication/ServerFolder/MyZipFile.zip.
If I go to the url and download the zip file I get the same error "File is corrupted". But if I manually go to the folder using file explorer and open the file then it works as expected.
Why and How?
And a long shot, try set the mime type to application/unknown, in this post there seems to be part of the solution to the posters problem:
http://jamesewelch.com/2008/12/03/sharpziplib-and-windows-extraction-wizard-errors/

Resources