File is broken when downloading from LinkButton - c#

On my ASP code, I have a LinkButton for my file upload:
<asp:Linkbutton ID="lnkContract" Text="" runat="server" Visible="false" onclick="lnkContract_Click"></asp:Linkbutton>
I manage to write a code in C# that triggers a file download in lnkContract_Click here:
protected void lnkContract_Click(object sender, EventArgs e)
{
string[] strFileType = lnkContract.Text.Split('.');
string strPath = Server.MapPath("~") + FilePath.CUST_DEALS + lnkContract.Text;
Open(lnkContract.Text, strFileType[1], strPath);
}
private void Open(string strFile, string strType, string strPath)
{
FileInfo fiPath = new FileInfo(#strPath);
//opens download dialog box
try
{
Response.Clear();
Response.ContentType = "application/" + strType.ToLower();
Response.AddHeader("Content-Disposition", "attachment; filename=\"" + strFile + "\"");
Response.AddHeader("Content-Length", fiPath.Length.ToString());
Response.TransmitFile(fiPath.FullName);
HttpContext.Current.ApplicationInstance.CompleteRequest();
Response.Clear();
}//try
catch
{
ucMessage.ShowMessage(UserControl_Message.MessageType.WARN, CustomerDefine.NOFILE);
}//catch if file is not found
}
when I click the LinkButton the file automatically downloads but when I open the file, it is broken (or if the file is .jpeg the file shows an "x"). Where did I go wrong?
Update
LinkButton is under UpdatePanel.

Instead of the second Response.Clear(); replace it with Response.End(); to flush the buffer and send all the data to the client.
You will have a problem with your code though, which is, that Response.End() actually causes a Thread abort exception, therefore, you should be more specific in the exception you catch.
UPDATE:
In your comments you mentioned that this is running within an UpdatePanel. In that scenario, this will not work. You will have to force that link button to execute a regular postback instead of an ajax one.
Here's how: https://stackoverflow.com/a/5461736/1373170

Try using this function that I'm shamelessly lifting from http://forums.asp.net/post/3561663.aspx to get the content type:
(Use it with your fiPath.Extension)
public static string GetFileContentType(string fileextension)
{
//set the default content-type
const string DEFAULT_CONTENT_TYPE = "application/unknown";
RegistryKey regkey, fileextkey;
string filecontenttype;
//the file extension to lookup
//fileextension = ".zip";
try
{
//look in HKCR
regkey = Registry.ClassesRoot;
//look for extension
fileextkey = regkey.OpenSubKey(fileextension);
//retrieve Content Type value
filecontenttype = fileextkey.GetValue("Content Type", DEFAULT_CONTENT_TYPE).ToString();
//cleanup
fileextkey = null;
regkey = null;
}
catch
{
filecontenttype = DEFAULT_CONTENT_TYPE;
}
//print the content type
return filecontenttype;
}

Related

Response.AddHeader seeing file path as file name

I have an ImageButton control within a ListView which, when clicked, should download the image with the right ID.
Here's the ImageButton ASPX:
<asp:ImageButton runat="server" ID="ibtDownloadImage" ImageUrl="img/downloadIcon.png" OnClick="ibtDownloadImage_OnClick" CommandArgument='<%# Convert.ToString(Eval("ID"))+Convert.ToString(Eval("FileExtension")) %>' />
As you can see, when it is clicked, it executes the "ibtDownloadImage_OnClick" method and sets the command argument to the ID plus the FileExtension (for example, 1.jpg, which is the name of the image).
My C# code for the ibtDownloadImageOnClick is:
protected void ibtDownloadImage_OnClick(object sender, EventArgs e)
{
ImageButton img = (ImageButton)sender;
string file = img.CommandArgument;
String imgURLtoDownload = #"img/uploads/"+file;
Response.AddHeader("Content-Disposition", "attachment; filename=" + imgURLtoDownload);
}
When I click the ImageButton control, it downloads a file called "img-uploads-1.jpg" (without the speech marks), so it seems to be taking what I intended to be the filepath as part of the name, and replacing the / with -...
Any ideas on how to fix this? It seems like it should be a simple solution.
I've run debugging with a breakpoint on the Response.AddHeader line and imgURLtoDownload's content is img/upload/1.jpg (as it should be)..
You can read file content as binary, let say you have function for this , that takes file name get byte array as binary content, and that function name GetFileContent(Filepath). Then you can use that function to write content to response and yet specify custom path.
ImageButton img = (ImageButton)sender;
string file = img.CommandArgument;
String imgURLtoDownload = #"img/uploads/"+file;
byte[] data= GetFileContent(Server.MapPath(imgURLtoDownload));
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=" + file);
Response.ContentType = System.Web.MimeMapping.GetMimeMapping(Server.MapPath(imgURLtoDownload));
Response.BinaryWrite(data);
Response.End();
public byte[] GetFileContent(string Filepath)
{
return System.IO.File.ReadAllBytes(Filepath);
}

GridView download files in Subfolders

I have a GridView with data in it, including filenames, I can download files only from one folder and cannot download the files in the subfolders. I have been using this
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Download")
{
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AppendHeader("Content-Disposition", "filename=" + e.CommandArgument);
Response.TransmitFile(Server.MapPath("Upload\\Track\\Files") + e.CommandArgument);
Response.End();
}
}
I have tried adding this, but I get an error stating its illegal.
string path = Server.MapPath("\\Upload\\Track\\Files\\ ,*," + SearchOption.AllDirectories);
Response.TransmitFile(path + e.CommandArgument);
I want to be able to download all files including the ones in the subfolders, using this method.
UPDATED -- I have also tried this way, but no success, I know I can get the right path, but it just doesn't download.
string path = Server.MapPath("\\Upload\\Track\\Files");
string filename = e.CommandArgument.ToString();
DirectoryInfo dir = new DirectoryInfo(path);
string pathString = System.IO.Path.Combine(dir + path);
if (e.CommandName == "Download")
{
System.IO.Directory.GetDirectories(path);
if (File.Exists(pathString)) {
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AppendHeader("Content-Disposition", "filename=" + filename);
Response.TransmitFile(pathString);
Response.End();
So I have found a solution to my question, I have added a field in my database which holds information about the path - I get this path from when I upload the files, which captures the path and adds it to the database. I have also added the below code to the uploads field in my GridView.
This solution is best for me at the moment, but of course can be improved.
<asp:TemplateField HeaderText="Quote" InsertVisible="false">
<ItemTemplate>
<%# Eval("Uploads") %>
</ItemTemplate>
</asp:TemplateField>

Download files based on their type, or how to give two options to Response.AppendHeader

I am allowing users to download either a PDF file or a zip file, and when they try to download the file, I want the appropriate file to be downloaded according to its type. For example: if the uploaded file is PDF, then it should be downloaded as a PDF; if the uploaded file is zip, then it should downloaded as a zip file.
I have written this code and I am able to download the files as PDF using "output.pdf" in the append header, but don't know how to give two options to append header so that it downloads the file according to its type.
protected void gridExpenditures_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Download")
{
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AppendHeader("content-disposition", "FileName=" + e.CommandArgument + "output.pdf");
Response.TransmitFile(Server.MapPath("~/Match/Files/") + e.CommandArgument);
Response.End();
}
}
You can use a utility like this one to detect the content type of the file in question, then render the header like this:
protected void gridExpenditures_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Download")
{
var filePath = Server.MapPath("~/Match/Files/") + e.CommandArgument;
var contentType = MimeTypes.GetContentType(filePath);
if (string.IsNullOrEmpty(contentType))
{
contentType = "application/octet-stream";
}
Response.Clear();
Response.ContentType = contentType;
Response.AppendHeader("content-disposition", "FileName=" + e.CommandArgument);
Response.TransmitFile(filePath);
Response.End();
}
}
You need to set your content type to the appropriate application, instead of octet-stream.
For example I had this to open PowerPoint:
application/vnd.openxmlformats-officedocument.presentationml.presentation
Look up your file type in this link:
http://en.wikipedia.org/wiki/Internet_media_type
I store the uploaded content type in my database for each file.

Download text as file in ASP.NET

I am trying to download some text output from the screen as a text file. Following is the code. It's working on some pages and not working at all on other pages. Can anyone please suggest what's wrong here?
protected void Button18_Click(object sender, EventArgs e){
Response.Clear();
Response.Buffer = true;
Response.ContentType = "text/plain";
Response.AppendHeader("content-disposition", "attachment;filename=output.txt");
StringBuilder sb = new StringBuilder();
string output = "Output";
sb.Append(output);
sb.Append("\r\n");
Response.Write(sb.ToString());
}
As already mentioned by Joshua, you need to write the text to the output stream (Response). Also, don’t forget to invoke Response.End() after that.
protected void Button18_Click(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
string output = "Output";
sb.Append(output);
sb.Append("\r\n");
string text = sb.ToString();
Response.Clear();
Response.ClearHeaders();
Response.AppendHeader("Content-Length", text.Length.ToString());
Response.ContentType = "text/plain";
Response.AppendHeader("Content-Disposition", "attachment;filename=\"output.txt\"");
Response.Write(text);
Response.End();
}
Edit 1: added more details
Edit 2: I was reading other SO posts where users were recommending to put quotes around the filename:
Response.AppendHeader("content-disposition", "attachment;filename=\"output.txt\"");
Source:
https://stackoverflow.com/a/12001019/558486
If that's your actual code, you never write the text to the response stream, so the browser never receives any data.
At the very least, you should need
Response.Write(sb.ToString());
to write your text data to the response. Also, as an added bonus, if you know the length beforehand you should provide it using the Content-Length header so the browser can show the download progress.
You're also setting Response.Buffer = true; as part of your method but never explicitly flush the response to send it to the browser. Try adding a Response.Flush() after your write statement.

When to delete generated file using asp.net

I have a template excel file to generate excel files from it.
My code is as follows (This part is to create a new excel file from the template):
string currentFN = PropertyFinalResult[0].Fecha;
string fixCurrentFN = currentFN.Replace('/', '_');
string currentTime = DateTime.Now.ToLongTimeString();
string fixCurrentTime = currentTime.Replace(':', '_');
string addToFileName = fixCurrentTime.Replace(' ', '_');
string newFN = fixCurrentFN + "-" + addToFileName;
string SourceFile = Request.PhysicalApplicationPath + "Template\\ExcelTemplate.xlsx";
string DestFile = Request.PhysicalApplicationPath + "Template\\" + newFN + ".xlsx";
//To keep FileName for posterior deletion
Session["sDestFile"] = DestFile;
try
{
File.Copy(SourceFile, DestFile);
}
catch (Exception ex)
{
lblErrorSavingToDB.Text = "Error: " + ex.Message;
lblErrorSavingToDB.Visible = true;
}
after that I open the new excel file, insert the records in it and then, stream the file to the user by doing this:
//Streaming file to client
string fileName = newFN + ".xlsx";
Response.Redirect("../Template/" + fileName);
Now, my question is, whether the user save or not the file, when should I delete the generated file? I would prefer once the user closes the popup window regarding Open or Save the file. But how to know when the user closes that window?
You can use TransmitFile and then close once the transmission is over. Example:
try
{
Response.ContentType = "application/octet-stream";
Response.AddHeader("content-disposition", "attachment;filename=\"" + Path.GetFileName(path.FullName) + "\"");
Response.AddHeader("content-length", path.Length.ToString());
Response.TransmitFile(path.FullName);
Response.Flush();
}
finally
{
File.Delete(Server.MapPath("~/"+tpacode+".zip"));
}
When to delete the files (or maybe it's better to say "how long to keep the files") is a question that is best answered by your application's business rules.
In the past, in low-traffic applications, I've used a "clean-up" routine to delete files older than a certain threshold. That clean-up gets performed when a new file is created, and at that time any file in the designated folder that was older than the threshold would be deleted.

Categories

Resources