I need the browser to open file types it understands directly in the browser (i.e. no "Open/Save/Cancel" dialog.
Here's my code, which currently works great!...except that every file pops up the dialog box and doesn't directly open the file:
string filePath = Path.Combine(WebConfigurationManager.AppSettings["NewsAttachmentPath"], context.Request.QueryString["FileName"]);
byte[] bytes = System.IO.File.ReadAllBytes(filePath);
context.Response.Clear();
context.Response.ContentType = "application/octet-stream";
context.Response.Cache.SetCacheability(HttpCacheability.Private);
context.Response.Expires = -1;
context.Response.Buffer = true;
context.Response.AddHeader("Content-Disposition", string.Format("{0};FileName=\"{1}\"", "inline", context.Request.QueryString["FileName"]));
context.Response.BinaryWrite(bytes);
context.Response.End();
As you can see, even when I change the Content-Disposition to "inline", it still prompts for download. This is with files that I know my browser understands. In other words, I can go to some random site and click a PDF, and it will open in the browser. My site will make me save it in order to view it.
Pre-emptive answer to "why do you wanna use application/octet-stream?" Because I don't want to create a handler for each single file type. If this is mistaken, please let me know.
You do not need to create a handler per file type. You just change the line:
context.Response.ContentType = "application/octet-stream";
to be:
string contentType = //your logic here, possibly many lines in a separate method
context.Response.ContentType = contentType;
But no: you can't "inline" an application/octet-stream. That means "here's some bytes, but I don't know what they are". The browser can't do much with that, other than save it somewhere, hence a download prompt. You can use content-disposition to suggest a filename, though.
The browser does not work on file extensions - it works on content-type. So: you need to report the correct content-type in your response. This might mean writing a switch / lookup based on the file extension that you know, or it might mean storing the explicit content-type separately as metadata along with the file information.
Related
In my MVC Core application, I have in my controller a way for the user to download an image that I have saved which is called from a button click in my view. I don't understand why I need the second variable "contentType" string in my return File(). I can still download the image as a .jpg if I put in "application/pdf" instead of "application/jpg", so what is even the point of having it other than the fact that it is required. What is the point of this variable?
public FileStreamResult Download()
{
string filename = "Capture.JPG";
string filepath = hostingEnvironment.WebRootPath + "\\Image";
string fullName = Path.Combine(filepath, filename);
return File(new FileStream(fullName, FileMode.Open), "application/pdf", filename);
}
Flydog's comment is quite pertinent; perhaps the most useful or obvious difference you'll find in declaring a content type is that the browser may use it to invoke a different behavior depending on the setting. If you send an mp4 video as "video/mp4" the browser may well play it within its viewport. If you send the same file as "application/octet-stream" it may offer a save as. If you claim one content type and it's actually another it's likely the browser will ignore you (note: claiming a pdf is pdf or octet stream are both reasonable/true. Claiming it's a jog is not). Content-Disposition also plays a part in the server aiming to give the browser some direction on what to do with the file..
..but primarily I'd say a good reason to tell the truth about the content type (ie don't claim it's a jpg when it's a pdf) is the interop mantra "when it sones to standards adherence, be strict in what you send and liberal in what you accept" - you're sending, so follow protocol :)
I am using ashx to serve images from a database, is there anyway to have a user click on a link that allows them to download the file on the computer. (IE it shows the Save Dialog) Like you download a file. Is this possible to do?
If you want it to prompt to save make sure you add the following line when creating the response:
context.Response.AppendHeader("Content-Disposition",
"attachment;filename=" + filename);
This will make the browser treat it like an attachment and prompt with the save dialog.
EDIT: Based on your comment make sure you are building your response correctly:
// set attachment header like above
// then you need to get your file in byte[] form
byte[] dataYouWantToServeUp = GetData();
// you can set content type as well
yourHttpContext.Response.ContentType = "image/jpg";
// serve up the response
yourHttpContext.Response.BinaryWrite(dataYouWantToServeUp);
I think that if the navigator is able to open type of the file you're trying to get, It will open it without asking. If for instants, the file is in zip format, the navigator is not able to open it and will ask you to download it.
I have a process that creates a PDF. I want these PDF's to be temporary and short lived. I want to be able to perform the following when the user clicks a button:
string CreatePDF()//returns fileName.pdf
PromptUserToDownloadPDF()
DeletePDF(fileName.pdf)
I want to avoid having to create a cleanup procedure and deal with any race conditions that arise from users concurrently creating PDF's while running cleanup.
In winforms, I would synchronously prompt a user to download a file. How can I do a similar task in web?
UPDATE
Please note that I am using a 3rd party app to create the PDF's (Apache FOP). Basically I (will) have a function that invokes the command line:
C:>fop "inputfile" "output.pdf"
So, in memory is not an option...that is unless I could somehow do like....
string CreatePDF()//returns fileName.pdf
string RecreatePDFInMemory()
DeletePDF(fileName.pdf)
PromptUserToDownloadPDF()
Something like this:
byte[] _pdfbytes = CreatePDF();
Response.ContentType = "application/pdf";
Response.AppendHeader("Content-Length", _pdfbytes.Length.ToString());
Response.BinaryWrite(_pdfbytes);
Since this creates the PDF in memory, you don't need to worry about cleanup.
Edit for OP's edit:
From within CreatePDF, You can use Path.GetTempFileName to create a temp file and execute "fop" to write to that file. Delete that file immediately before returning the byte[]. I recommend doing this delete inside of a finally block. However, "Fop" does support having its output piped to stdout. Having the CreatePDF function grab that is probably cleaner.
Look into doing something along these lines.
Similar to what someone referred to in a different answer, you don't need to save the PDF file on your system, you can just send it as a response.
I'm not sure how you're creating your PDF, but try looking into this below and seeing if your process could use something like this.
HttpResponse currentResponse = HttpContext.Current.Response;
currentResponse.Clear();
currentResponse.ClearHeaders();
currentResponse.ContentType = "application/pdf";
currentResponse.AppendHeader("Content-Disposition", "attachment; filename=my.pdf");
//create the "my.pdf" here
currentResponse.Flush();
currentResponse.End();
Not sure of your process but you should be able to write the PDF to a byte[] and skip writing to the disk altogether.
byte[] pdf = GetPDFBytes(filename)
MemoryStream pdfStream = new MemoryStream(pdf);
Then use the pdfStream to send back to a user.
You can stream out a file with an asp.net page.
I tried to find very old article for you which demonstrates this with a GIF (there's not an actual file)
http://www.informit.com/articles/article.aspx?p=25487
It makes a special page which streams out the data (sets content type appropriately in the header).
Similarly, you can make a "page" to stream out the PDF - it might not even need to ever reside on disk, but if it did, you could delete it after streaming it to the browser.
I want to allow my user to click on a button that says "download" and then will build a dynamic csv file on the fly and will prompt the user to download. I know how to build the file using a HTTPHandler, but not quite sure the mechanism for delivering the content via download.
I usually create a Generic Handler (.ashx) and do something like this:
public void ProcessRequest(HttpContext context)
{
StringBuilder myCsv = new StringBuilder();
foreach(myRow r in myRows) {
myCsv.AppendFormat("\"{0}\",{1}", r.MyCol1, r.MyCol2);
}
context.Response.ContentType = "application/csv";
context.Response.AddHeader("content-disposition", "attachment; filename=myfile.csv");
context.Response.Write(myCsv.ToString());
}
Now if the CSV you're building is REALLY big, you might want to write out each row as you create it, rather than stuffing it into a StringBuilder.
Just emit a Content-Disposition HTTP header in your HTTP Handler code. Most browsers will interpret that as a download request. Replace yourfile.csv by the filename you want the user to see.
context.Response.ContentType = "text/csv";
context.Response.AddHeader("Content-Disposition", "attachment; filename=yourfile.csv");
I'm trying to download remote file using C# (ASP.NET). The problem is when I browse to the file download URL - it downloads perfectly. When I try the WebClient.DownloadData(url) I get "no data to show" response.
If I browse using the built-in VS2010 browser I still get this "error" message.
The file link is: http://www.tase.co.il/TASE/Pages/Export.aspx?sn=he-IL_ds&enumTblType=AllSecurities&Columns=he-IL_Columns&Titles=he-IL_Titles&TblId=0&ExportType=3
(CSV file)
How can the file be downloaded? any ideas?
Many Thanks
This isn't a download problem on your side, that page is referencing something, probably from session...so you would need to login to the site, get a session, basically go through whatever steps you normally would in route to getting the file so it's generated correctly/available.
If I had to take a guess, I'm thinking they do the export to the file system when you ask for it, so it's a temp file somewhere...not something you can just grab, so unless you went through the step that created it (doesn't seem this Export.aspx page does it), it won't be there, and their error message for this is "No data to show".
There's also the possibility they're explicitly blocking you to prevent any kind of bots/leeching from happening...basically preventing exactly what you're trying to do.
Many sites will block downloads based on the Referer or User-Agent header.
Watch a "working" download with
Fiddler
Watch your code's
download with Fiddler
Compare the
two requests
Update your request
to match the "working" case.
I solved the problem by obtaining the remote file as a stream. From that point I was able to read/write the stream to the response.
//The absolute path to your file
string remoteFile = "http://my.cloudfront.net/videos/vehicle_english_v3.mp4";
//The name of the file you want the user to see when they download the file
int pos = remoteFile.LastIndexOf("/") + 1;
string fileName = remoteFile.Substring(pos, remoteFile.Length - pos);
//Obtain your file as a stream
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(remoteFile);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream fileStream = response.GetResponseStream();
//Write the stream to the response
if (fileStream != null && fileStream.CanRead) {
context.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
context.Response.ContentType = "application/octet-stream";
context.Response.ClearContent();
fileStream.CopyTo(context.Response.OutputStream);
}