I need to stream a file which will result in save as prompt in the browser.
The issue is, the directory that the file is located is virtually mapped, so I am unable to use Server.MapPath to determine it’s actual location. The directory is not in the same location (or even phyical server on the live boxes) as the website.
I’d like something like the following, but that will allow me to pass a web URL, and not a server file path.
I may have to end up building my file path from a config base path, and then append on the rest of the path, but hopefully I can do it this way instead.
var filePath = Server.MapPath(DOCUMENT_PATH); if (!File.Exists(filePath)) return; var fileInfo = new System.IO.FileInfo(filePath); Response.ContentType = "application/octet-stream"; Response.AddHeader("Content-Disposition", String.Format("attachment;filename="{0}"", filePath)); Response.AddHeader("Content-Length", fileInfo.Length.ToString()); Response.WriteFile(filePath); Response.End();
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
You could use HttpWebRequest to get the file and stream it back to the client. This allows you to get the file with a url. An example of this that I found ( but can’t remember where to give credit ) is
//Create a stream for the file Stream stream = null; //This controls how many bytes to read at a time and send to the client int bytesToRead = 10000; // Buffer to read bytes in chunk size specified above byte[] buffer = new Byte[bytesToRead]; // The number of bytes read try { //Create a WebRequest to get the file HttpWebRequest fileReq = (HttpWebRequest) HttpWebRequest.Create(url); //Create a response for this request HttpWebResponse fileResp = (HttpWebResponse) fileReq.GetResponse(); if (fileReq.ContentLength > 0) fileResp.ContentLength = fileReq.ContentLength; //Get the Stream returned from the response stream = fileResp.GetResponseStream(); // prepare the response to the client. resp is the client Response var resp = HttpContext.Current.Response; //Indicate the type of data being sent resp.ContentType = MediaTypeNames.Application.Octet; //Name the file resp.AddHeader("Content-Disposition", "attachment; filename="" + fileName + """); resp.AddHeader("Content-Length", fileResp.ContentLength.ToString()); int length; do { // Verify that the client is connected. if (resp.IsClientConnected) { // Read data into the buffer. length = stream.Read(buffer, 0, bytesToRead); // and write it out to the response's output stream resp.OutputStream.Write(buffer, 0, length); // Flush the data resp.Flush(); //Clear the buffer buffer = new Byte[bytesToRead]; } else { // cancel the download if client has disconnected length = -1; } } while (length > 0); //Repeat until no data is read } finally { if (stream != null) { //Close the input stream stream.Close(); } }
Method 2
Download url to bytes and convert bytes into stream:
using (var client = new WebClient()) { var content = client.DownloadData(url); using (var stream = new MemoryStream(content)) { ... } }
Method 3
I do this quite a bit and thought I could add a simpler answer. I set it up as a simple class here, but I run this every evening to collect financial data on companies I’m following.
class WebPage { public static string Get(string uri) { string results = "N/A"; try { HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri); HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); StreamReader sr = new StreamReader(resp.GetResponseStream()); results = sr.ReadToEnd(); sr.Close(); } catch (Exception ex) { results = ex.Message; } return results; } }
In this case I pass in a url and it returns the page as HTML. If you want to do something different with the stream instead you can easily change this.
You use it like this:
string page = WebPage.Get("http://finance.yahoo.com/q?s=yhoo");
Method 4
2 years later, I used Dallas’ answer, but I had to change the HttpWebRequest
to FileWebRequest
since I was linking to direct files. Not sure if this is the case everywhere, but I figured I’d add it. Also, I removed
var resp = Http.Current.Resonse
and just used Http.Current.Response
in place wherever resp
was referenced.
Method 5
If you are looking for a .NET Core version of @Dallas’s answer, use the below.
Stream stream = null; //This controls how many bytes to read at a time and send to the client int bytesToRead = 10000; // Buffer to read bytes in chunk size specified above byte[] buffer = new Byte[bytesToRead]; // The number of bytes read try { //Create a WebRequest to get the file HttpWebRequest fileReq = (HttpWebRequest)HttpWebRequest.Create(@"file url"); //Create a response for this request HttpWebResponse fileResp = (HttpWebResponse)fileReq.GetResponse(); if (fileReq.ContentLength > 0) fileResp.ContentLength = fileReq.ContentLength; //Get the Stream returned from the response stream = fileResp.GetResponseStream(); // prepare the response to the client. resp is the client Response var resp = HttpContext.Response; //Indicate the type of data being sent resp.ContentType = "application/octet-stream"; //Name the file resp.Headers.Add("Content-Disposition", "attachment; filename=test.zip"); resp.Headers.Add("Content-Length", fileResp.ContentLength.ToString()); int length; do { // Verify that the client is connected. if (!HttpContext.RequestAborted.IsCancellationRequested) { // Read data into the buffer. length = stream.Read(buffer, 0, bytesToRead); // and write it out to the response's output stream resp.Body.Write(buffer, 0, length); //Clear the buffer buffer = new Byte[bytesToRead]; } else { // cancel the download if client has disconnected length = -1; } } while (length > 0); //Repeat until no data is read } finally { if (stream != null) { //Close the input stream stream.Close(); } }
Method 6
You could try using the DirectoryEntry class with the IIS path prefix:
using(DirectoryEntry de = new DirectoryEntry("IIS://Localhost/w3svc/1/root" + DOCUMENT_PATH)) { filePath = de.Properties["Path"].Value; } if (!File.Exists(filePath)) return; var fileInfo = new System.IO.FileInfo(filePath); Response.ContentType = "application/octet-stream"; Response.AddHeader("Content-Disposition", String.Format("attachment;filename="{0}"", filePath)); Response.AddHeader("Content-Length", fileInfo.Length.ToString()); Response.WriteFile(filePath); Response.End();
Method 7
The accepted solution from Dallas was working for us if we use Load Balancer on the Citrix Netscaler (without WAF policy).
The download of the file doesn’t work through the LB of the Netscaler when it is associated with WAF as the current scenario (Content-length not being correct) is a RFC violation and AppFW resets the connection, which doesn’t happen when WAF policy is not associated.
So what was missing was:
Response.End();
See also:
Trying to stream a PDF file with asp.net is producing a “damaged file”
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0