I'm developing a video website using ASP.NET MVC.
One functionality I want to have in my application is transocding video. But as the transcoding process could be very time-consuming, I want to show the progress of that process to the client user.
So, my schema is to use one controller action to handle the whole transcoding process and write its progress into a file stored on the server. Meanwhile I use Ajax to call another controller action to read the specified file, retrieve the progress information and send it back to the client for display every 2 seconds during the transcoding process.
To fulfill my plan, I have written the following code:
Server Side:
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 });
}
}
Client Side:
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 retrieving 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
...
}
});
}
Now the Client-side code and the Transcode
action method all work fine. The problem is that the GetProgress
method will never get called until the Transcode
action finishes its whole procedure. So what's wrong with my code? How can I modify it to make those two actions work spontaneously so as to achieve my goal?
Update
Based on Alex's answer, I found that my problem is caused by the session lock mechanism of Asp.Net framework. So disabling the SessionState
of my VideoController
or setting it as read-only does make the controller responses to the request for retrieving transcoding progress when the action method of transcoding video is being executed. But because I use Session
in my VideoController
to store some variables for use across multiple requests, this way couldn't be a suitable solution for my problem. Is there a better way to solve it?
For example: If use ajax request in other ajax page, Using ajax in php loop, etc, Give you multiple ajax request with one result. I have solution: Use window. onload = function() { ... }
If you had two server side page for your two AJAX files then it would be fine. Try to create two different aspx file for you different methods to have them called together.
The jQuery library provides a way to make any callback function waiting for multiple Ajax calls. This method is based on an object called Deferred. A Deferred object can register callback functions based on whether the Deferrred object is resolved or rejected.
To iterate through the response, there is a callback function attached to it. This callback function gets executed once both the Ajax requests are finished. In case where multiple Deferred objects are passed to $. when() , it takes the response returned by both calls, and constructs a new promise object.
Run multiple AJAX requests in parallel When working with APIs, you sometimes need to issue multiple AJAX requests to different endpoints. Instead of waiting for one request to complete before issuing the next, you can speed things up with jQuery by requesting the data in parallel, by using jQuery's $.when () function:
Or the “Deferred” way, this time using a shorthand $.get () method: But we have three Ajax requests we’re needing to perform, and we want to wait for all three of them to finish before doing anything, so it could get pretty gnarly in callback land:
So you decide that instead of having that HTML, CSS, and JavaScript on the page directly, you’re going to Ajax that stuff in when the feature is about to be used. We’ll need to make three Ajax requests.
It’s slow. One request … wait to be done … another request … wait to be done … another request … wait to be done … go. It would be faster if we could do: All three requests in parallel … wait for all three to be done … go.
You misunderstood the whole point about async/await. It doesn't change the fact that for each single request, there is a single response that is returned. When you call await in your action, nothing is returned to client yet. The only thing it does (in a very high level abstraction) is to release the current thread that handles this request to a thread pool so it could be used for processing other requests. So basically it allows you to use your server resources more efficiently since there are no threads that are wasted waiting for long I/O operations to complete. Once the I/O operation is completed the the execution of the action (that called for await) continued. Only at the end of an action the response is sent to the client.
As for you scenario, if it is a long running task, I would use some kind of background processing solution such as Hangfire and use SignalR to push updates from server.Here is an example
You can also implement something similar on your own (example).
UPDATE: As @Menahem stated in his comment I maybe misinterpreted part of your question.
Request queuing issue may be caused by incorrect configuration of SessionStateBehavior. Since MvcHandler handler which is used by ASP.NET MVC is marked with IRequiresSessionState interface, only one request at time could be processed per session. In order to change that, make you controller sessionless (or ar least make sure that you are not writing into session in this controller) and mark it with
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
attribute.
File creation is blocking call. In other words, until first thread will not close file, second one which makes report will not be able to read contents of that file. As workaround you can create files with percentage of progress. For example movie1-5-percent.txt, movie1-10-percent.txt, movie1-15-percent.txt etc, in order to avoid blocking calls to file system. Then you can check, if for movie1 there is file movie1-15-percent.txt, then you can report to ajax call, that 15 percent of movie was converted. Or choose another non blocking storage. For example you can report result to db in first thread, and read results from db in another.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With