我正在使用ASP.NET MVC开发视频网站。
我想在我的应用程序中使用的一项功能是转换视频。但由于转码过程可能非常耗时,我想向客户端用户展示该过程的进度。
因此,我的架构是使用一个控制器操作来处理整个转码过程并将其进度写入存储在服务器上的文件中。同时我使用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",
type: "POST",
//dataType: 'JSON',
success: function (data)
{
if (data.progress == undefined || data.progress == null)
return;
progressPerc = parseFloat(data.progress);
//Update progress bar
...
}
});
}
现在,客户端代码和Transcode
操作方法都可以正常工作。问题是GetProgress
方法永远不会被调用,直到Transcode
操作完成整个过程。那么我的代码有什么问题呢?如何修改它以使这两个动作自发地工作以实现我的目标?
更新:
根据Alex的回答,我发现我的问题是由Asp.Net框架的会话锁定机制引起的。因此,禁用SessionState
的{{1}}或将其设置为只读会使控制器响应在执行转码视频的操作方法时检索转码进度的请求。但是因为我在VideoController
中使用Session
来存储一些变量以供跨多个请求使用,这种方式对我的问题来说不是一个合适的解决方案。有没有更好的方法来解决它?
答案 0 :(得分:8)
你误解了关于异步/等待的全部观点。它不会改变这样一个事实:对于每个单个请求,都会返回一个响应。当您在操作中调用await时,尚未向客户端返回任何内容。它所做的唯一事情(在非常高级别的抽象中)是将处理此请求的当前线程释放到线程池,以便它可以用于处理其他请求。因此,基本上它允许您更有效地使用服务器资源,因为没有浪费的线程等待长I / O操作完成。一旦I / O操作完成,就继续执行动作(要求等待)。只有在操作结束时,响应才会发送给客户端。
至于你的场景,如果它是一个长期运行的任务,我会使用某种后台处理解决方案,如Hangfire,并使用SignalR从服务器推送更新。Here is an example
你也可以自己实现类似的东西(example)。
<强>更新强>: 正如@Menahem在评论中所说,我可能误解了你的部分问题。
请求排队问题可能是由SessionStateBehavior的错误配置引起的。由于ASP.NET MVC使用的MvcHandler处理程序标有IRequiresSessionState接口,因此每个会话只能处理一个请求。为了改变它,让控制器无会话(或者至少确保你没有写入这个控制器中的会话)并用它来标记它
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
属性。
答案 1 :(得分:1)
文件创建阻止了呼叫。换句话说,直到第一个线程不会关闭文件,第二个生成报告的线程将无法读取该文件的内容。作为解决方法,您可以创建具有进度百分比的文件。例如movie1-5-percent.txt,movie1-10-percent.txt,movie1-15-percent.txt等,以避免阻塞对文件系统的调用。然后你可以检查,如果movie1有文件movie1-15-percent.txt,那么你可以向ajax调用报告,15%的电影被转换。或者选择其他非阻塞存储。例如,您可以在第一个线程中向db报告结果,在另一个线程中从db读取结果。