I am trying to build a functionality to download a csv file in C#.
When the name of the file has non-english character, the downloaded file does not seems to have the correct name. However in the network tab, the response header has the same Content-Disposition value, as given in the code.
Sample Code
private void PopulateCsvInResponse(MemoryStream csvData, string fileName)
{
HttpResponse response = HttpContext.Current.Response;
response.Clear();
//actual file name "Москва.csv"
response.AddHeader("Content-Disposition", "attachment; filename=%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0.csv");
byte[] byteArray = csvData.ToArray();
response.AddHeader("Content-Length", byteArray.Length.ToString());
response.ContentType = "text/csv; charset=utf-8";
response.BinaryWrite(byteArray);
response.Flush();
response.Close();
}
For example the file name is Москва.csv.
UTF-8 encoded name : %D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0.csv.
Things that I tried
Replacing Content-Disposition header
Attempt 1
response.AddHeader("Content-Disposition",
"attachment; filename=Москва.csv");
The downloaded file name is
Ð_оÑ_ква
Attempt 2
response.AddHeader("Content-Disposition",
"attachment; filename=\"%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0.csv\"; filename*=UTF-8''%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0.csv");
The downloaded file name is
_%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0.csv_; filename_
Attempt 3
response.AddHeader("Content-Disposition",
"attachment; filename=%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0.csv");
The downloaded file name is
%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0.csv
Attempt 4
response.AddHeader("Content-Disposition",
"attachment; filename*=UTF-8''%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0.csv");
The downloaded file name is
UTF-8''%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0.csv
I finally found out the solution.
The issue was never with above code, it was always working fine.
The actual issue was in front end, where the content disposition header that was received in encode was not decoded, and I skipped to see this part when I raised the question.
I thought of deleting this question, but keeping it so that if someone makes the same silly mistake as me, might realise it earlier instead of wasting time to look for solution for the problem that never existed.
Related
I'm newbie on asp.net, i am developing Online Report Generator, i want to let the client to choose where he/she want to put his/her files so this is my code but didn't seems to work, it returning to my ajax error statement my ConfigurationManager.AppSettings["ExtractFolder"] is equal to "c:\temp\": just want to ask what's wrong with the code?
context.Response.ContentType = "application/vnd.ms-excel";
context.Response.AddHeader("content-disposition", "attachment;filename="+filename);
context.Response.WriteFile(ConfigurationManager.AppSettings["ExtractFolder"]+filename);
context.Response.Flush();
context.Response.End();
Try this
String FileName = "FileName.txt";
String FilePath = "C:/...."; //Replace this
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.ContentType = "text/plain";
response.AddHeader("Content-Disposition", "attachment; filename=" + FileName + ";");
response.TransmitFile(FilePath);
response.Flush();
response.End();
For more content types check our http://en.wikipedia.org/wiki/MIME_type#List_of_common_media_types
Basically you can't do what you want to do the way you are trying to do it.
The ASP.net code that you have is dependant on being part of a full http Request/Response cycle. The part where it is setting headers is where it telling the browser to download stuff.
An AJAX request is looking for a specific return with its request, generally javascript passable data. You are not returning this but rather the contents of a file.
Here's an article the explains the concepts and problems a little more. It also provides you an alternate solution. Basically the solution revolves around using an iFrame to handle the file download request.
First, check that your source file you want to send to the user is in c:\temp\ and that you have set filename to the name of the file you are sending. Next you will want to make sure that your .net process has permission to c:\temp on your server. It probably doesn't.
Also, make sure you understand that response.writefile actually reads a file from the server and writes it out to the browser. You can't specify where the user saves the file to locally, this is handled by the browser not by your code.
Using Coders sample code make sure you change the following (see my comments)
String FileName = "FileName.xlsx"; //MAKE SURE THIS IS YOUR EXCEL FILENAME ON YOUR SERVER
String FilePath = "C:/...."; //SET THIS TO THE FOLDER THE FILE IS IN (put your file in your root folder of your website for now, you can move it later once you get the download code working)
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; //MAKE SURE YOU HAVE CHANGED THIS TOO
response.AddHeader("Content-Disposition", "attachment; filename=" + FileName + ";");
response.TransmitFile(FilePath);
response.Flush();
response.End();
I also suggest trying the above in a standalone page first then incorporate it into your AJAX page once you know its working.
I've got basic functionality to stream a file to the browser which invokes a "Save As". The output is dynamically generated and stored within a string and not a file saved on the server.
See code below.
string output = GenerateCSVDdata;
Response.Clear();
Response.ClearHeaders();
Response.AddHeader("Content-Disposition", "attachment; filename=\"test.csv\");
Response.ContentType = "application/octet-stream";
Response.BinaryWrite(System.Text.Encoding.UTF8.GetPreamble());
Response.Write(output);
Response.End();
Now, on my development server, the CSV fully downloads. On the production server, the last few characters on end are cut off. The larger the CSV, the more characters are missing. I've tried so many different things like Response.Flush etc but nothing can fix it. The only thing I can do is throw a load of empty chars on the end in hope nothing gets cut.
Is there something quite wrong with this method of streaming a file download without actually saving the file to disk?
Thanks for your help.
Can you determine if there is a difference in the byte count for the .csv file you are using?
byte[] defaultEncodingBytes = System.Text.Encoding.Default.GetBytes(defaultEncodingFileContents);
byte[] UTF8EncodingBytes = System.Text.Encoding.UTF8.GetBytes(defaultEncodingFileContents);
Try this, it worked for me.
void DownloadFile(string filename)
{
//string filename = "c:\\temp\\test.csv";
byte[] contents = System.IO.File.ReadAllBytes(filename);
Response.Clear();
Response.ClearHeaders();
Response.AppendHeader("Content-disposition", String.Format("attachment; filename=\"{0}\"", System.IO.Path.GetFileName(filename)));
Response.AppendHeader("Content-Type", "binary/octet-stream");
Response.AppendHeader("Content-length", contents.Length.ToString());
Response.BinaryWrite(contents);
if (Response.IsClientConnected)
Response.Flush();
}
Regards.
I have got this WebService that allows uploading/downloading any docs (mostly .docx, .doc, .pdf) and all it returns is byte[] when querying for downloading.
I have written this code
string ContractGUID = dtContract.Rows[0]["ContractGUID"].ToString();
//Get Bytes from WebService
byte[] fileData = BLL.Contract.GetDocument(new Guid(ContractGUID));
Response.Clear();
Response.BinaryWrite(fileData);
Response.AddHeader("Content-Disposition", "Attachment");
Response.Flush();
The other methods that the WebService exposed are GetDocumentName and GetDocumentLen
Is it possible to determine the Mime-Type or force the browser to download it in the right format? Currently it is downloading as .htm in Chrome and when open, I see funny characters. Any better advice?
Thanks.
No, it's not possible to force the browser to download in the right format without you telling it via the Content-Type header.
Response.ContentType = "application/pdf"; //or whatever appropriate
If the web service exposes a GetDocumentName() method you can probably infer the appropriate format by looking at the name, assuming the name has a file extension. This, obviously, is not bullet proof since you can change the extension of a file to anything you want.
Another alternative would be to try and guess the file format by peeking at the first bytes. For example, if the first 4 bytes of the file are 25 50 44 46 then it's very likely that this is a PDF file. On this website, they have a pretty extensive list.
Here's the list of possible content-type headers.
I think, the browser does it through filename.
e.g.:
response.Clear();
response.AddHeader("Content-Disposition", "attachment; filename=" + dbFile.filename.Replace(" ", "_"));
response.AddHeader("Content-Length", dbFile.data.Length.ToString());
response.ContentType = "application/octet-stream";
response.OutputStream.Write(dbFile.data, 0, dbFile.data.Length);
response.End();
dbFile.filename is a string
dbFile.data is a byte[]
When I use the following code to download a ZIP file it appears to work. However, when I attempt to open the downloaded ZIP, I get an 'invalid compressed folder' message. When I open the ZIP in notepad I see it is filled with HTML.
string fp = Server.MapPath("directory\\file.zip");
FileInfo file = new FileInfo(fp);
if (file.Exists)
{
Response.ClearContent();
Response.AddHeader("content-disposition","attachment; filename=" + file.Name);
Response.AddHeader("content-length", file.Length.ToString());
Response.ContentType = "application/zip";
Response.TransmitFile(file.FullName);
Response.End();
}
An issue I can't seem to fix that is probably related is when I try to manually type in the address of the file (http://website.com/downloads/file.zip), I get a redirect (http://website.com/login.aspx) even when logged in as the admin. Any pointers in where to look would be greatly appreciated.
Instead of just using Response.ClearContent() also use Response.ClearHeaders() to remove all the current headers as well as the body of the response.
From MSDN, HttpResponse.ClearContent Method:
The ClearContent method does not clear header information.
How can I specify the filename when dumping data into the response stream?
Right now I'm doing the following:
byte[] data= GetFoo();
Response.Clear();
Response.Buffer = true;
Response.ContentType = "application/pdf";
Response.BinaryWrite(data);
Response.End();
With the code above, I get "foo.aspx.pdf" as the filename to save. I seem to remember being able to add a header to the response to specify the filename to save.
Add a content-disposition to the header:
Response.AddHeader("content-disposition", #"attachment;filename=""MyFile.pdf""");
FYI... if you use "inline" instead of "attachment" the file will open automatically in IE. Instead of prompting the user with a Open/Save dialogue.
Response.AppendHeader("content-disposition", string.Format("inline;FileName=\"{0}\"", fileName));
Response.AppendHeader("Content-Disposition", "attachment; filename=foo.pdf");
For some reason, most of the answers out there don't seem to even attempt to encode the file name value. If the file contains spaces, semicolons or quotes, it mightn't come across correctly.
It looks like you can use the ContentDisposition class to generate a correct header value:
Response.AppendHeader("Content-Disposition", new ContentDisposition
{
FileName = yourFilename
}.ToString());
You can check out the source code for ContentDisposition.ToString() to confirm that it's trying to encode it properly.
Warning: This seems to crash when the filename contains a dash (not a hyphen). I haven't bothered looking into this yet.
Response.AddHeader("Content-Disposition", "attachment;filename=" & FileName & ";")