简体   繁体   中英

How can I redirect to another action from my Asynchronous Action in MVC4

I am currently trying to write some code that will accept some FTP details, get a file list, then allow the user to download the files.

This all works fine, except that I have to wait for the files to finished downloading before it will do anything. I am using an Asynchronous controller as I thought this was meant to help with this sort of thing.

Please see a selection of my code below, I have left out all the code thats not relevant:

  [HttpPost]
    public ActionResult FtpAsync(Downloads model)
    {
        var ftpAddress = model.Url;
        if (!ftpAddress.StartsWith("ftp://"))
            ftpAddress = String.Concat("ftp://", ftpAddress);

        var serverPath = Server.MapPath(ThumbnailSupport.CreateBaseVirtualPathForClient(StandardFileLinks.DropBoxLocation,
                                                                        _website.Client));

        if (!Directory.Exists(serverPath))
            Directory.CreateDirectory(serverPath);

        foreach (string file in model.SelectedFiles)
        {

            var webClient = new WebClient();

            AsyncManager.OutstandingOperations.Increment();

            AsyncManager.Parameters["FilesToDownload"] = model.SelectedFiles;
            webClient.Credentials = new NetworkCredential(model.Username, model.Password);

            webClient.DownloadFileAsync(new Uri(ftpAddress + "/" + file), serverPath + "\\" + file);
            AsyncManager.OutstandingOperations.Decrement();
        }

        return RedirectToAction("Transfer");

    }


    public ActionResult FtpCompleted(Downloads model)
    {
        return RedirectToAction("Complete");
    }

    public ActionResult Transfer()
    {
        return PartialView();
    }

It fires the FtpCompleted action perfectly fine, but the problem is this is meant to handle file transfers of potentially GB's of information. I dont want the user to sit watching a spinning disc while they wait for the files to be downloaded. Hence the reason I was trying to redirect them to the Transfer Action, this action just displays a message telling them that the transfer may take a while and they will be notified once its complete. However this action never actually gets called. I step through the code in debug and it calls it, but it never displays the message and it never gets to the browser according to FireBug.

Am I doing something stupid or is it just not possible to do this?

I would be grateful for any assistance people can offer here as I am completely stuck after browsing google and other posts on here. Even my boss who is a much more experienced coder isnt sure how to handle this.

Thanks in advance,

Gaz

As explained in the documentation an asynchronous controller action consists of 2 methods:

  1. A method which returns void and has the Async suffix and the action name prefix
  2. A method which returns ActionResult and has the Completed suffix and the action name prefix

The first method triggers asynchronous operation and returns immediately. The second method is invoked once the entire operation has completed.

So here's are the correct action signatures:

[HttpPost]
public void FtpAsync(Downloads model)
{
    ...
}

public ActionResult FtpCompleted(Downloads model)
{
    return Content("Complete");
}    

Now, if you don't want to freeze the browser during the entire operation you will have to invoke the first controller action using AJAX. Asynchronous controllers do not change anything in terms of the HTTP protocol. From the client perspective it is absolutely the same. The only difference with a standard controller action is that you are not jeopardizing a worker thread during the entire operation. You are now relying on the IOCP (IO/Completion Ports) that are an OS artifact used by the WebClient . The idea is that when you start an IO intensive operation, an IOCP port is created and the controller action immediately returns the thread to the ASP.NET thread pool. Then the operation could last for hours, and once it completes the IOCP is signaled, a pool is drawn from the thread pool and the Completed action is invoked on this thread. So in terms of threads it is very effective. But the total time of execution is absolutely the same as if you have used a standard controller action. Many people think that because Asynchronous Controller are called Asynchronous they run asynchronously from the client perspective. That's a false impression. Asynchronous actions don't make your operations miraculously run faster. They are still perfectly synchronous from the client perspective, because that's how the HTTP protocol works.

So you have 2 possibilities:

  • Invoke the controller action using AJAX to avoid blocking the browser.
  • Use COMET/PUSH technology in which it is the server that notifies the client. For example in HTML5 there's the WebSockets standard which could be used to achieve that. In the .NET world SignalR is a great framework which encapsulates this PUSH technology.

The second approach is recommended if you want a really effective solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM