我有一个系统,用户可以从MVC3应用程序中上传有时大的(100-200 MB)文件。我想在文件上传时不阻止UI,经过一些研究,看起来新的AsyncController可能让我做我正在尝试做的事情。问题是 - 我所看到的每一个例子并没有真正做同样的事情,所以我似乎错过了一个关键的部分。经过多次充实和摆弄之后,这是我目前的代码:
public void CreateAsync(int CompanyId, FormCollection fc)
{
UserProfile up = new UserRepository().GetUserProfile(User.Identity.Name);
int companyId = CompanyId;
// make sure we got a file..
if (Request.Files.Count < 1)
{
RedirectToAction("Create");
}
HttpPostedFileBase hpf = Request.Files[0] as HttpPostedFileBase;
if (hpf.ContentLength > 0)
{
AsyncManager.OutstandingOperations.Increment();
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, e) =>
{
string fileName = hpf.FileName;
AsyncManager.Parameters["recipientId"] = up.id;
AsyncManager.Parameters["fileName"] = fileName;
};
worker.RunWorkerCompleted += (o, e) => { AsyncManager.OutstandingOperations.Decrement(); };
worker.RunWorkerAsync();
}
RedirectToAction("Uploading");
}
public void CreateCompleted(int recipientId, string fileName)
{
SystemMessage msg = new SystemMessage();
msg.IsRead = false;
msg.Message = "Your file " + fileName + " has finished uploading.";
msg.MessageTypeId = 1;
msg.RecipientId = recipientId;
msg.SendDate = DateTime.Now;
SystemMessageRepository.AddMessage(msg);
}
public ActionResult Uploading()
{
return View();
}
现在这里的想法是让用户提交文件,调用后台进程,它会做很多事情(为了测试目的,现在只是拉动文件名),同时将它们指向上传视图,它只是说“你的文件正在上传......继续,我们会在它准备好时通知你”。 CreateCompleted方法通过将消息插入用户的消息队列来处理该通知。
所以问题是,我从来没有得到上传视图。相反,我得到一个空白的创建视图。我无法弄清楚为什么。是否因为CreateCompleted方法被调用而显示Create视图?如果它返回无效,为什么会这样做呢?我只是希望它在后台静默执行,插入一条消息并停止。
这是采取ALL的正确方法吗?我这样做的全部原因是有一些网络速度,上传文件可能需要30分钟,而在当前版本中,它会阻止整个应用程序直到完成。如果我可以避免它,我宁愿不使用类似弹出窗口的东西,因为它会遇到弹出阻塞脚本等一堆支持问题。
无论如何 - 我没有想法。建议?救命?我可能考虑的替代方法?
提前致谢。
答案 0 :(得分:2)
你在这里做错了。假设您的操作名称为Create
。
CreateAsync
将捕获请求,并且应该是一个void方法并且不返回任何内容。如果您有属性,则应将它们应用于此方法。 CreateCompleted
是您应该将其视为标准控制器操作方法的方法,您应该在此方法中返回ActionResult
。以下是一个简单的示例:
[HttpPost]
public void CreateAsync(int id) {
AsyncManager.OutstandingOperations.Increment();
var task = Task<double>.Factory.StartNew(() => {
double foo = 0;
for(var i = 0;i < 1000; i++) {
foo += Math.Sqrt(i);
}
return foo;
}).ContinueWith(t => {
if (!t.IsFaulted) {
AsyncManager.Parameters["headers1"] = t.Result;
}
else if (t.IsFaulted && t.Exception != null) {
AsyncManager.Parameters["error"] = t.Exception;
}
AsyncManager.OutstandingOperations.Decrement();
});
}
public ActionResult CreateCompleted(double headers1, Exception error) {
if(error != null)
throw error;
//Do what you need to do here
return RedirectToAction("Index");
}
另请注意,此方法仍将阻止操作完成。这不是一场火灾,而是忘记&#34;键入异步操作。
有关详细信息,请查看:
Using an Asynchronous Controller in ASP.NET MVC
修改强>
您想要的是以下代码。忘记所有AsyncController的东西,这是你的创建动作post方法:
[HttpPost]
public ActionResult About() {
Task.Factory.StartNew(() => {
System.Threading.Thread.Sleep(10000);
if (!System.IO.Directory.Exists(Server.MapPath("~/FooBar")))
System.IO.Directory.CreateDirectory(Server.MapPath("~/FooBar"));
System.IO.File.Create(Server.MapPath("~/FooBar/foo.txt"));
});
return RedirectToAction("Index");
}
请注意,我在那里等了10秒才能让它真实。发帖后,您会看到它会立即返回而无需等待。然后,打开你的应用程序的根文件夹并观看。您会注意到10秒后将创建一个文件夹和文件。
但是(一个大的),这里没有异常处理,逻辑如何通知用户等。
如果我是你,我会在这里看一个不同的方法,或者让用户忍受等待。