ASP.Net: Download files from server with their original name


Introduction :

Purpose of writing this post is to share a way to resolve a very common but important issue with uploading-downloading file in ASP.Net. I don’t claim it to be the most efficient way but it works for me.

Problem:

While uploading a file to the server we may save it with its original file name and may code like-


FileUploader1.SaveAs(Server.MapPath("UploadedFiles/" + FileUploader1.FileName));

But, problem is if we upload another file with same name as previous file then the previous one will be replaced with the latest one. Now all the links for downloading both theses files will reference to the same file on the server.
Again we may resolve this issue by saving files with unique name on the server and may code like-

FileUploader1.SaveAs(Server.MapPath("UploadedFiles/" + DateTime.Now.ToString("yyyyMMddhhmmssfffffff") + System.IO.Path.GetExtension(FileUploader1.FileName);));

By doing this we will lose the original file name forever. But, in some or, more scenarios user may expect the file to be downloaded to his/her hard-disk with the original file name with which he/she uploaded the file to the server. (Example: Uploading-Downloading multiple resumes of a candidate).

We can solve this issue by storing both the names of a file i,e original & unique name on server.

The actual headache now comes in to picture. File is saved on the server with some unique name but we want the to change it’s name to the original name while user downloading it to his/her hard-disk.

Solution:

[View Demo]

The above said problem can be solved by following few steps. For the illustration I am taking a Gridview to show files for downloading and LinkButton for the download links.

  • Store both actual and unique names of the file in Database
  • Use a Hidden Field to store actual(unique) file name in the gridview
    <asp:TemplateField HeaderText="Uploaded File">
     <ItemTemplate>
     <asp:LinkButton ID="lnkDownload" Text='<%#Eval("FileNameOriginal")%>' runat="server" OnClick="lnkDownload_Click" />
     <asp:HiddenField ID="hfDownload" runat="server" Value='<%#Eval("FileNameActual")%>' />
     </ItemTemplate>
     </asp:TemplateField>
    
    
  • Now, put following code in the link button click event
    LinkButton lnkDwnload = (LinkButton)sender;
     GridViewRow grow = (GridViewRow)lnkDwnload.Parent.Parent;
     HiddenField hfDownload = (HiddenField)grow.FindControl("hfDownload");
     Response.AddHeader("Content-Disposition", "attachment;filename=" + lnkDwnload.Text);
     Response.ContentType = GetContentType(Server.MapPath("~/UploadedFiles/" + hfDownload.Value.ToString()));
     Response.TransmitFile(Server.MapPath("~/UploadedFiles/" + hfDownload.Value.ToString()));
     Response.End();
    
    

Note that I have used a method GetContentType() to get the content type/MIME type of the file to be downloaded.


private string GetContentType(string fileName)
 {
 string contentType = "application/octetstream";
 string ext = System.IO.Path.GetExtension(fileName).ToLower();
 Microsoft.Win32.RegistryKey registryKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
 if (registryKey != null && registryKey.GetValue("Content Type") != null)
 contentType = registryKey.GetValue("Content Type").ToString();
 return contentType;
 }

Note : Microsoft.Win32 is OS specific MSDN

For a universal solution we may use another way of getting the MIME type.


private string GetContentType(string fileName)
 {
 string ext = System.IO.Path.GetExtension(fileName).ToLower();
 switch (fileName)
 {

//add as many extensions of file types you expect to be uploaded

//for a complete list you may visit http://stackoverflow.com/questions/1029740/get-a-mime-from-an-extension

case ".doc": return "application/msword";

case ".docx": return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";

case ".jpeg": return "image/jpeg";

case ".jpg": return "image/jpeg";

case ".bmp": return "image/bmp";

default: return "application/octet-stream";

}
}

Conclusion:

This works very fine for me. If you are facing problem then drop a comment, it will be my pleasure to help you. Any better idea is welcome.

Thanks for reading !

Advertisements

SQL Server: Delete/Update duplicate records


Problem:

My third article in Problem-Solution approach to keep it short and simple.

When you insert two records with same values for each of the columns in a table and then if you want to delete one of those two records then you may face problems like if you open the table data in SSMS and try to delete one of those rows by right clicking then it will not work. If you try to delete with a simple delete query then both the duplicate records will be deleted. You will face the same problem while updating those records also. Lets examine it using a simple example.

Example:

CREATE TABLE Demo (Id INT,Name VARCHAR(20));

INSERT INTO Demo VALUES(1,'TEST1');
INSERT INTO Demo VALUES(2,'TEST2');
INSERT INTO Demo VALUES(3,'TEST3');
INSERT INTO Demo VALUES(3,'TEST3');

Now, try to delete/update one of the record having Id=3.

The query for Update may be like-


UPDATE Demo SET Name='3Test' WHERE Id=3

The query for Delete may be like-


DELETE Demo WHERE Id=3

Solution:

Solution for Update


SET ROWCOUNT 1
UPDATE Demo SET Name='3Test' WHERE Id=3
SET ROWCOUNT 0

Solution for Delete


SET ROWCOUNT 1
DELETE FROM Demo WHERE Id=3
SET ROWCOUNT 0

Analysis:

According to MSDNSET ROWCOUNT Causes SQL Server to stop processing the query after the specified number of rows are returned.  So, the above query will affect only one record and this is what we needed.

[+] ADDED LATER [+]
[Thanks Manas Ranjan Dash for bringing this to notice]
As the MSDN link says “Using SET ROWCOUNT will not affect DELETE, INSERT, and UPDATE statements in the next release of SQL Server”, lets try some other alternative solutions.
Solution 2:
Using TOP –>

DELETE TOP (1) FROM Demo WHERE Id=3 AND Name='Test3'

This query will delete 1 record and you can change it as required.
Solution 3:
Using Common Table Expression & Ranking Functions –>

;WITH CTE(Id,Name,SlNo)
AS
(
	SELECT Id,Name,ROW_NUMBER() OVER(ORDER BY Id) AS SlNo
	FROM Demo
)
DELETE FROM CTE WHERE SlNo > 1 AND Id=3 AND Name='Test3'

The above script will leave only a single record with Id=3 and Name=’Test3′.

Hopefully this article may be useful to you. Please put your comments and suggestions.

Thanks !!!