Response.AddHeader seeing file path as file name - c#

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);
}

Related

How to generate txt file then force download in ASP.NET Web Forms?

I need to set a handler to <asp:Button> element. When user clicks, txt file must be generated in code-behind and must be automatically downloaded by user. There is no need to get any data from user. All file content will be generated in code behind. For example, how to return to user a txt file with content of this variable:
string s = "Some text.\nSecond line.";
For this kind of job you should create file on the fly.
Please see the code:
protected void Button_Click(object sender, EventArgs e)
{
string s = "Some text.\r\nSecond line.";
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=testfile.txt");
Response.AddHeader("content-type", "text/plain");
using (StreamWriter writer = new StreamWriter(Response.OutputStream))
{
writer.WriteLine(s);
}
Response.End();
}
}
Just note for new line you need to use \r\n instead of only \n or use WriteLine function for each line
You can simply generate the file on the server side and then push it down to the user. s would be the content of the File that you will generate.
Creating the File
Creating a new File is as simple as writing the data to it,
// var s that you're having
File.Create(Server.MapPath("~/NewFile.txt")).Close();
File.WriteAllText(Server.MapPath("~/NewFile.txt"), s);
This will create a new file (if doesn't exist) and write the content of the variable s to it.
Pushing it down to the user
You can allow the user to download it, using the following code,
// Get the file path
var file = Server.MapPath("~/NewFile.txt");
// Append headers
Response.AppendHeader("content-disposition", "attachment; filename=NewFile.txt");
// Open/Save dialog
Response.ContentType = "application/octet-stream";
// Push it!
Response.TransmitFile(file);
This will let him have the file you just created.
I do have a similar case but a little bit more complex usecase.
I do generate different types of files. I wrote a container class Attachment witch holds the content type and the value of the generated file as an Base64 string.
public class Attachment {
public string Name {get;set;}
public string ContentType {get;set;}
public string Base64 {get;set;}
}
This enables me to serve different file types with the same download method.
protected void DownloadDocumentButton_OnClick(object sender, EventArgs e) {
ASPxButton button = (ASPxButton) sender;
int attachmentId = Convert.ToInt32(button.CommandArgument);
var attachment = mAttachmentService.GenerateAttachment(attachmentId);
Response.Clear();
Response.AddHeader("content-disposition", $"attachment; filename={attachment.Name}");
Response.AddHeader("content-type", attachment.ContentType);
Response.BinaryWrite(Convert.FromBase64String(attachment.Base64));
Response.End();
}

Download PDF file from a Directory Listing

I have used a Gridview Control to display the contents of a directory in asp.net webforms.
The contents are filtered to display only PDF files.
I also have a Button inside a TemplateField. On the click of the button the user should be able to download and save the PDF file.
The columns displayed in the Gridview are File Name, Modified Date and Size.
How can I program the Button click to download and save the PDF file?
I have a function that performs a file download.
public static void DownloadFile(string FilePath, System.Web.HttpResponse response)
{
System.IO.FileInfo file = new System.IO.FileInfo(FilePath);
if ((file.Exists))
{
response.Clear();
response.AddHeader("Content-Disposition", "attachment; filename=" + file.Name);
response.AddHeader("Content-Length", file.Length.ToString());
response.ContentType = "application/octet-stream";
response.WriteFile(file.FullName);
response.End();
response.Close();
file = null;
}
}
The FilePath parameter is the physical path, so if you have the virtual path (e.g. ~/Folder/file.pdf) might need to use the Server.MapPath(...) function to call the function.
In your Button click event, write the following code.
protected void Button1_Click(object sender, EventArgs e)
{
Response.ContentType = "Application/pdf";
Response.AppendHeader("Content-Disposition", "attachment; filename=Your_Pdf_File.pdf");
Response.TransmitFile(Server.MapPath("~/Files/Your_Pdf_File.pdf"));
Response.End();
}

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.

File is broken when downloading from LinkButton

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;
}

Categories

Resources