Я пытался найти существующее расширение, но не сразу нашел его (возможно, у меня слабая функция поиска).
Я сразу подумал, что вам нужно создать два новых класса.
Сначала создайте класс, унаследованный от 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);