我正在尝试从ASP.NET MVC向iPhone客户端提供视频文件.视频格式正确,如果我在一个可公开访问的网络目录中可以正常工作.
从我所看到的核心问题是,iPhone需要您有一个简易的下载环境,让您可以通过HTTP头过滤字节范围.我认为这是为了让用户可以通过视频跳过.
当使用MVC提供文件时,这些标题不存在.我试图效仿它,但没有运气.我们在这里有IIS6,我根本无法做许多标题操作. ASP.NET会抱怨说“这个操作需要IIS集成管道模式”.
升级不是一个选项,我不允许将文件移动到公共Web共享.我感觉到我们的环境受到限制,但我正在寻找解决方案.
以下是我正在努力做的一些示例代码
public ActionResult Mobile(string guid = "x") { guid = Path.GetFileNameWithoutExtension(guid); apMedia media = DB.apMedia_GetMediaByFilename(guid); string mediaPath = Path.Combine(Transcode.Swap_MobileDirectory,guid + ".m4v"); if (!Directory.Exists(Transcode.Swap_MobileDirectory)) //Make sure it's there... Directory.CreateDirectory(Transcode.Swap_MobileDirectory); if(System.IO.File.Exists(mediaPath)) return base.File(mediaPath,"video/x-m4v"); return Redirect("~/Error/404"); }
我知道我需要这样做,但是我无法在.NET MVC中做到这一点. http://dotnetslackers.com/articles/aspnet/Range-Specific-Requests-in-ASP-NET.aspx
以下是HTTP响应头的示例:
Date Mon,08 Nov 2010 17:02:38 GMT Server Apache Last-Modified Mon,08 Nov 2010 17:02:13 GMT Etag "14e78b2-295eff-4cd82d15" Accept-Ranges bytes Content-Length 2711295 Content-Range bytes 0-2711294/2711295 Keep-Alive timeout=15,max=100 Connection Keep-Alive Content-Type text/plain
这里是一个不是(这是从.NET)的示例
Server ASP.NET Development Server/10.0.0.0 Date Mon,08 Nov 2010 18:26:17 GMT X-AspNet-Version 4.0.30319 X-AspNetMvc-Version 2.0 Content-Range bytes 0-2711294/2711295 Cache-Control private Content-Type video/x-m4v Content-Length 2711295 Connection Close
有任何想法吗?谢谢.
解决方法
更新:现在是
project on CodePlex.
好的,我在本地的测试台上工作,我可以将视频流传输到我的iPad.这有点肮脏,因为它比我预期的要困难得多,而且现在工作我没有时间来清理它.关键部件:
行动过滤器
public class ByteRangeRequest : FilterAttribute,IActionFilter { protected string RangeStart { get; set; } protected string RangeEnd { get; set; } public ByteRangeRequest(string RangeStartParameter,string RangeEndParameter) { RangeStart = RangeStartParameter; RangeEnd = RangeEndParameter; } public void OnActionExecuting(ActionExecutingContext filterContext) { if (filterContext == null) throw new ArgumentNullException("filterContext"); if (!filterContext.ActionParameters.ContainsKey(RangeStart)) filterContext.ActionParameters.Add(RangeStart,null); if (!filterContext.ActionParameters.ContainsKey(RangeEnd)) filterContext.ActionParameters.Add(RangeEnd,null); var headerKeys = filterContext.RequestContext.HttpContext.Request.Headers.AllKeys.Where(key => key.Equals("Range",StringComparison.InvariantCultureIgnoreCase)); Regex rangeParser = new Regex(@"(\d+)-(\d+)",RegexOptions.Compiled); foreach(string headerKey in headerKeys) { string value = filterContext.RequestContext.HttpContext.Request.Headers[headerKey]; if (!string.IsNullOrEmpty(value)) { if (rangeParser.IsMatch(value)) { Match match = rangeParser.Match(value); filterContext.ActionParameters[RangeStart] = int.Parse(match.Groups[1].ToString()); filterContext.ActionParameters[RangeEnd] = int.Parse(match.Groups[2].ToString()); break; } } } } public void OnActionExecuted(ActionExecutedContext filterContext) { } }
基于FileStreamResult的自定义结果:
public class ContentRangeResult : FileStreamResult { public int StartIndex { get; set; } public int EndIndex { get; set; } public long TotalSize { get; set; } public DateTime LastModified { get; set; } public FileStreamResult(int startIndex,int endIndex,long totalSize,DateTime lastModified,string contentType,Stream fileStream) : base(fileStream,contentType) { StartIndex = startIndex; EndIndex = endIndex; TotalSize = totalSize; LastModified = lastModified; } public override void ExecuteResult(ControllerContext context) { if (context == null) throw new ArgumentNullException("context"); HttpResponseBase response = context.HttpContext.Response; response.ContentType = this.ContentType; response.AddHeader(HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderContentRange),string.Format("bytes {0}-{1}/{2}",StartIndex,EndIndex,TotalSize)); response.StatusCode = 206; 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; FileStream.Seek(StartIndex,SeekOrigin.Begin); while (bytesRemaining > 0) { if (bytesRemaining <= buffer.Length) count = FileStream.Read(buffer,bytesRemaining); else count = FileStream.Read(buffer,buffer.Length); outputStream.Write(buffer,count); bytesRemaining -= count; } } } }
我的MVC动作:
[ByteRangeRequest("StartByte","EndByte")] public FileStreamResult NextSegment(int? StartByte,int? EndByte) { FileStream contentFileStream = System.IO.File.OpenRead(@"C:\temp\Gets.mp4"); var time = System.IO.File.GetLastWriteTime(@"C:\temp\Gets.mp4"); if (StartByte.HasValue && EndByte.HasValue) return new ContentRangeResult(StartByte.Value,EndByte.Value,contentFileStream.Length,time,"video/x-m4v",contentFileStream); return new ContentRangeResult(0,(int)contentFileStream.Length,contentFileStream); }
我真的希望这有帮助.我花了大量的时间在这个!你可能想要尝试的一件事是删除碎片,直到它再次破裂.看看ETag的东西,修改的日期等是否可以被删除是很好的.我现在没有时间.
快乐编码!