Download/Stream file from URL - asp.net

C#asp.netFile IoStreaming

C# Problem Overview


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

C# Solutions


Solution 1 - C#

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

Solution 2 - C#

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))
    {
        ...
    }
}   

Solution 3 - C#

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

Solution 4 - C#

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.

Solution 5 - C#

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

Solution 6 - C#

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

Solution 7 - C#

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: https://stackoverflow.com/questions/1300729/trying-to-stream-a-pdf-file-with-asp-net-is-producing-a-damaged-file

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionmp3duckView Question on Stackoverflow
Solution 1 - C#DallasView Answer on Stackoverflow
Solution 2 - C#BerezhView Answer on Stackoverflow
Solution 3 - C#CodeChopsView Answer on Stackoverflow
Solution 4 - C#TingoView Answer on Stackoverflow
Solution 5 - C#anonymousView Answer on Stackoverflow
Solution 6 - C#Andreas PaulssonView Answer on Stackoverflow
Solution 7 - C#Oliver FinkView Answer on Stackoverflow