我想在我的应用程序中使用的一项功能是转换视频.但由于转码过程可能非常耗时,我想向客户端用户展示该过程的进度.
因此,我的架构是使用一个控制器操作来处理整个转码过程并将其进度写入存储在服务器上的文件中.同时我使用Ajax调用另一个控制器操作来读取指定的文件,检索进度信息并将其发送回客户端,以便在转码过程中每2秒显示一次.
为了实现我的计划,我编写了以下代码:
服务器端:
public class VideoController : Controller { //Other action methods .... //Action method for transcoding a video given by its id [HttpPost] public async Task<ActionResult> Transcode(int vid=0) { VideoModel VideoModel = new VideoModel(); Video video = VideoModel.GetVideo(vid); string src = Server.MapPath("~/videos/")+video.Path; string trg = Server.MapPath("~/videos/") + +video.Id+".mp4"; //The file that stores the progress information string logPath = Server.MapPath("~/videos/") + "transcode.txt"; string pathHeader=Server.MapPath("../"); if (await VideoModel.ConvertVideo(src.Trim(),trg.Trim(),logPath)) { return Json(new { result = "" }); } else { return Json(new { result = "Transcoding Failed,please try again." }); } } //Action method for retrieving the progress value from the specified log file public ActionResult GetProgress() { string logPath = Server.MapPath("~/videos/") + "transcode.txt"; //Retrive the progress from the specified log file. ... return Json(new { progress = progress }); } }
客户端:
var progressTimer = null; var TranscodeProgress=null; //The function that requests server for handling the transcoding process function Transcode(vid) { //Calls the Transcode action in VideoController var htmlobj = $.ajax({ url: "/Video/Transcode",type: "POST",//dataType: 'JSON',data: { 'vid': vid },success: function(data) { if(data.result!="") alert(data.result); } else { //finalization works .... } } }); //Wait for 1 seconds to start retrieving transcoding progress progressTimer=setTimeout(function () { //Display progress bar ... //Set up the procedure of retireving progress every 2 seconds TranscodeProgress = setInterval(Transcoding,2000); },1000); } //The function that requests the server for retrieving the progress information every 2 seconds. function Transcoding() { //Calls the GetProgress action in VideoController $.ajax({ url: "/Video/GetProgress",success: function (data) { if (data.progress == undefined || data.progress == null) return; progressPerc = parseFloat(data.progress); //Update progress bar ... } }); }
现在,客户端代码和Transcode操作方法都可以正常工作.问题是,在Transcode操作完成整个过程之前,GetProgress方法永远不会被调用.那我的代码出了什么问题?如何修改它以使这两个动作自发地工作以实现我的目标?
更新:
根据Alex的回答,我发现我的问题是由Asp.Net框架的会话锁定机制引起的.因此,禁用我的VideoController的SessionState或将其设置为只读会使控制器响应在执行转码视频的操作方法时检索转码进度的请求.但是因为我在我的VideoController中使用Session来存储一些变量以供跨多个请求使用,这种方式不适合我的问题.有没有更好的方法来解决它?
解决方法
至于你的场景,如果它是一个长时间运行的任务,我会使用某种后台处理解决方案,如Hangfire,并使用SignalR从server.Here is an example推送更新
你也可以自己实现类似的东西(example).
更新:
正如@Menahem在评论中所说,我可能误解了你的一部分问题.
请求排队问题可能是由于SessionStateBehavior的错误配置引起的.由于ASP.NET MVC使用的MvcHandler处理程序标有IRequiresSessionState接口,因此每个会话只能处理一个请求.为了改变它,让控制器无会话(或者至少确保你没有写入这个控制器中的会话)并用它来标记它[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]属性.