由于我想实现从 ASP.NET Core 后端下载大尺寸文件(> 4GB),许多文章指出 .NET Framework 中的
HttpResponse.TransmitFile
可以实现我的目标。
但是,
HttpResponse.TransmitFile
在.NET Core中似乎不再可用。
有谁知道 .NET Core 中
HttpResponse.TransmitFile
的替代品是什么?我无法告诉你我是多么感谢你的相关回答。
我怀疑真正的问题不是找到
TransmitFile
的替代方案(它是return File(path)
或return File(stream)
,而是处理请求范围,以便客户端可以分块下载大文件,如果中断可以重试。
幸运的是,自 ASP.NET Core 2.1 以来可用的 ControllerBase.File 方法和 Minimal API(以及其他)中使用的 Results.File 方法都已经支持这一点。范围处理默认关闭,但可以通过将
true
传递给 enableRangeProcessing
参数来启用,例如:
public class VideoController : Controller
{
[HttpGet, Route("videos/video.mp4")]
public IActionResult Index()
{
return File("d:\Videos\video.mp4", "video/mp4", true);
}
}
更好的是,静态文件提供程序还支持开箱即用的范围(和响应压缩)。如果大文件位于特定文件夹中,您可以使用以下命令提供它们:
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider("path\to\large\files"),
RequestPath = "/Videos"
});
如果您想在自己的操作中使用 响应压缩,您必须在 Web 服务器上启用它或通过响应压缩中间件显式启用它:
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
});
var app = builder.Build();
app.UseResponseCompression();
从那时起,客户端就需要检索特定的块并重试它们。下载实用程序通常会分块下载大文件,并自动重试失败的部分。 Khalid Abuhakmeh 在一篇简短的博客文章中描述了该过程以及它如何与 ASP.NET Core 一起工作。
在 C# 中,HttpClient 可以请求文件的特定块,甚至可以使用Range
标头同时下载它们,例如:
var req = new HttpRequestMessage
{
RequestUri = new Uri( url )
};
req.Headers.Range = new RangeHeaderValue( 0, 999 );
var resp = await client.SendAsync(req);
if (resp.IsSuccessStatusCode)
{
using var tempFile=File.Create("chunk.001");
await resp.Content.CopyToAsync(tempFile);
}
ASP.NET Core 上的 Streaming Zip。
private static HttpClient Client { get; } = new HttpClient();
[HttpGet]
public async Task<FileStreamResult> Get()
{
// get your stream
var stream = await Client.GetStreamAsync("https://raw.githubusercontent.com/StephenClearyExamples/AsyncDynamicZip/master/README.md");
return new FileStreamResult(stream, new MediaTypeHeaderValue("text/plain"))
{
FileDownloadName = "README.md"
};
}
对于拉链:
private static HttpClient Client { get; } = new HttpClient();
[HttpGet]
public IActionResult Get()
{
var filenamesAndUrls = new Dictionary<string, string>
{
{ "README.md", "https://raw.githubusercontent.com/StephenClearyExamples/AsyncDynamicZip/master/README.md" },
{ ".gitignore", "https://raw.githubusercontent.com/StephenClearyExamples/AsyncDynamicZip/master/.gitignore" },
};
return new FileCallbackResult(new MediaTypeHeaderValue("application/octet-stream"), async (outputStream, _) =>
{
using (var zipArchive = new ZipArchive(new WriteOnlyStreamWrapper(outputStream), ZipArchiveMode.Create))
{
foreach (var kvp in filenamesAndUrls)
{
var zipEntry = zipArchive.CreateEntry(kvp.Key);
using (var zipStream = zipEntry.Open())
using (var stream = await Client.GetStreamAsync(kvp.Value))
await stream.CopyToAsync(zipStream);
}
}
})
{
FileDownloadName = "MyZipfile.zip"
};
}
该解决方案与我们之前的非核心解决方案具有相同的优点: