我尝试寻找现有的扩展,但没有立即找到(可能我的 search-fu 很弱。)
我的直接想法是你需要创建两个新课程。
首先,创建一个继承自ActionMethodSelectorAttribute 的类。这是HttpGet、HttpPost 等的同一个基类。在这个类中,您将覆盖IsValidForRequest。在该方法中,检查标头以查看是否请求了范围。您现在可以使用此属性来装饰控制器中的方法,当有人请求流的一部分(iOS、Silverlight 等)时将调用该方法
其次,创建一个继承自ActionResult 或FileResult 的类,并覆盖ExecuteResult 方法以添加您为要返回的字节范围确定的标头。像返回一个 JSON 对象一样返回它,其中包含字节范围开始、结束、总大小的参数,以便它可以正确生成响应标头。
查看FileContentResult 的实现方式,了解如何访问上下文的HttpResponse 对象以更改标头。
查看HttpGet 以了解它如何实现对IsValidForRequest 的检查。 CodePlex 上提供了源代码,或者您可以像我刚才那样使用 Reflector。
您可以使用此信息进行更多搜索,看看是否有人已经创建了此自定义 ActionResult。
作为参考,AcceptVerbs 属性如下所示:
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
string httpMethodOverride = controllerContext.HttpContext.Request.GetHttpMethodOverride();
return this.Verbs.Contains<string>(httpMethodOverride, StringComparer.OrdinalIgnoreCase);
}
这就是 FileResult 的样子。注意AddHeader的使用:
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = this.ContentType;
if (!string.IsNullOrEmpty(this.FileDownloadName))
{
string headerValue = ContentDispositionUtil.GetHeaderValue(this.FileDownloadName);
context.HttpContext.Response.AddHeader("Content-Disposition", headerValue);
}
this.WriteFile(response);
}
我只是把它拼凑起来。我不知道它是否适合您的需求(或有效)。
public class ContentRangeResult : FileStreamResult
{
public int StartIndex { get; set; }
public int EndIndex { get; set; }
public int TotalSize { get; set; }
public ContentRangeResult(int startIndex, int endIndex, string contentType, Stream fileStream)
:base(fileStream, contentType)
{
StartIndex = startIndex;
EndIndex = endIndex;
TotalSize = endIndex - startIndex;
}
public ContentRangeResult(int startIndex, int endIndex, string contentType, string fileDownloadName, Stream fileStream)
: base(fileStream, contentType)
{
StartIndex = startIndex;
EndIndex = endIndex;
TotalSize = endIndex - startIndex;
FileDownloadName = fileDownloadName;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
if (!string.IsNullOrEmpty(this.FileDownloadName))
{
System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition() { FileName = FileDownloadName };
context.HttpContext.Response.AddHeader("Content-Disposition", cd.ToString());
}
context.HttpContext.Response.AddHeader("Accept-Ranges", "bytes");
context.HttpContext.Response.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", StartIndex, EndIndex, TotalSize));
//Any other headers?
this.WriteFile(response);
}
protected override void WriteFile(HttpResponseBase response)
{
Stream outputStream = response.OutputStream;
using (this.FileStream)
{
byte[] buffer = new byte[0x1000];
int totalToSend = EndIndex - StartIndex;
int bytesRemaining = totalToSend;
int count = 0;
while (bytesRemaining > 0)
{
if (bytesRemaining <= buffer.Length)
count = FileStream.Read(buffer, 0, bytesRemaining);
else
count = FileStream.Read(buffer, 0, buffer.Length);
outputStream.Write(buffer, 0, count);
bytesRemaining -= count;
}
}
}
}
像这样使用它:
return new ContentRangeResult(50, 100, "video/x-m4v", "SomeOptionalFileName", contentFileStream);