Download/Stream file from URL - asp.net

2019-01-01 15:33发布

问题:

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

回答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 = \"application/octet-stream\";

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


回答2:

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


回答3:

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


回答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.



回答5:

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