MVC has a lot of great built in tooling, including the ability to stream very large file results straight from disk without having to load the whole file stream into memory.
What about the scenario where you want to stream a large file from one web server to another?
For example, I have an ASP.NET MVC application that needs to expose a download for a file hosted on another server, but I can not just redirect my users directly to the other URL. For that, we need to create a custom ActionResult type!
WebRequestFileResult
Here is a simple of example of what your controller might look like:
public class FileController : Controller
{
public ActionResult LocalFile()
{
return new FilePathResult(@"c:\files\otherfile.zip", "application/zip");
}
public ActionResult RemoteFile()
{
return new WebRequestFileResult("http://otherserver/otherfile.zip");
}
}
Here is the implementation for WebRequestFileResult:
public class WebRequestFileResult : FileResult
{
private const int BufferSize = 32768; // 32 KB
private const string DispositionHeader = "Content-Disposition";
private const string LengthHeader = "Content-Length";
private readonly string _url;
public WebRequestFileResult(string url)
: base("application/octet-stream")
{
if (String.IsNullOrWhiteSpace(url))
throw new ArgumentNullException("url", "Url is required");
_url = url;
}
protected override void WriteFile(HttpResponseBase localResponse)
{
var webRequest = WebRequest.Create(_url);
using (var remoteResponse = webRequest.GetResponse())
using (var remoteStream = remoteResponse.GetResponseStream())
{
if (remoteStream == null)
throw new NullReferenceException(
"Request returned null stream: " + _url);
var dispositionKey = remoteResponse.Headers.AllKeys.FirstOrDefault(
k => k.Equals(
DispositionHeader,
StringComparison.InvariantCultureIgnoreCase));
if (!String.IsNullOrWhiteSpace(dispositionKey))
localResponse.AddHeader(
DispositionHeader,
remoteResponse.Headers[DispositionHeader]);
var contentLenthString = remoteResponse.ContentLength
.ToString(CultureInfo.InvariantCulture);
localResponse.ContentType = remoteResponse.ContentType;
localResponse.AddHeader(LengthHeader, contentLenthString);
var buffer = new byte[BufferSize];
var loopCount = 0;
while (true)
{
if (!localResponse.IsClientConnected)
break;
var read = remoteStream.Read(buffer, 0, BufferSize);
if (read <= 0)
break;
localResponse.OutputStream.Write(buffer, 0, read);
// Flush after 1 MB; note that this
// will prevent server side caching.
loopCount++;
if (loopCount % 32 == 0)
localResponse.Flush();
}
}
}
}
Enjoy,
Tom
You should use Stream.Copy method instead of while(true) loop.
ReplyDeleteSorry, Stream.CopyTo
DeleteWhen using Stream.CopyTo the response stream would not always auto flush, and the server could built up a lot of memory pressure.
DeleteHow can filestreamresult support resume download from url , Tom ?
DeleteYou completed certain reliable points there. I did a search on the subject and found nearly all persons will agree with your blog. gostream
ReplyDeleteWe are very excited to say that in Q2 2020 (April 1 to June 30) we saw more community involvement than ever before. Many pull requests were submitted that spanned from bug fixes for our low-level assembly to higher-level modules such as the AppKit framework. Thanks to everyone for your contributions and we hope for this level of engagement to continue.
ReplyDeleteGOM Player Plus crack
idm crack
Inpixio Photo Studio Ultimate Crack
Malwarebytes Anti Exploit Premium Crack
IObit Driver Booster Pro Crack