我有一个调用此功能的下载按钮:
public FileResult DownloadExport()
{
string fileName = "example";
// Activate 'In progress'
// Call to a function that takes a while
// Deactivate 'In progress'
return File(fileName, System.Net.Mime.MediaTypeNames.Application.Octet, Path.GetFileName(fileName));
}
所以,我调用一个为我生成文件的函数。此功能需要一段时间,我不希望我的用户认为应用程序崩溃。这就是为什么我想在用户等待时显示“进行中”的原因。我该如何实现呢?
澄清:这个问题不关于下载的进度,但关于生成文件的函数的进度。
答案 0 :(得分:5)
我最近遇到了同样的问题,我相信我的解决方案(根据this answer解决相关问题)就是您在寻找这个问题的原因。基本思想是在生成文件时显示进度微调器(在我的情况下),在文件生成完成时隐藏进度微调器,然后提供要下载的文件。要做到这一点,我需要做四件事:
首先,控制器上的操作需要将文件存储在Session
中[HttpPost]
public ActionResult RunReport(ReportViewModel viewmodel)
{
// Process that generates the object while will become the file here
// ...
using (var stream = new MemoryStream())
{
// Convert the object to a memory stream
report.Generate(stream); // Use your object here
string handle = Guid.NewGuid().ToString();
Session[handle] = stream.ToArray();
return new JsonResult()
{
Data = new
{
FileGuid = handle,
MimeType = "application/pptx",
FileName = "My File.pptx"
}
};
}
}
控制器还需要一个新动作,它将提供实际的下载文件
public ActionResult Download(string fileGuid, string mimeType, string filename)
{
if(Session[fileGuid] != null)
{
byte[] data = Session[fileGuid] as byte[];
Session.Remove(fileGuid); // Cleanup session data
return File(data, mimeType, filename);
}
else
{
// Log the error if you want
return new EmptyResult();
}
}
接下来,从视图中显示进度微调器的AJAX调用,调用RunReport(需要很长时间的操作),使用它返回的JSON数组返回下载文件(这是一个快速操作),然后再次隐藏旋转器。
<script type="text/javascript">
function RunReport(reportUrl) {
$.ajax({
cache: false,
url: reportUrl,
type: "POST",
success: function(response) {
window.location = "/Report/Download?fileGuid=" + response.FileGuid +
"&mimeType=" + response.MimeType + "&filename=" + response.FileName;
$("#progress-spinner").hide();
}
});
$("#progress-spinner").show();
}
</script>
最后,启动它的链接并生成用于AJAX调用的动作链接
<a href="javascript: RunReport('@Url.Action("RunReport", "UserReport", new { ReportId = Model.Id })')">Run Report</a>
我希望这有助于某人!
答案 1 :(得分:2)
上面的“XMLHttpRequest”解决方案对我不起作用。文件保存对话框从未出现过。
以下是我们团队提出的解决方案:
而不是返回FileResult,返回JSON。并将文件流放入Session变量。
public ActionResult SetupExport()
{
var fileName = //your file here;
Session[fileName] = baseOutputStream;
return Json(new { success = true, fileName }, JsonRequestBehavior.AllowGet);
}
在控制器中创建另一个方法,将Session传递给FileStreamResult
public FileStreamResult DownloadExport()
{
var file = (Stream) Session[fileName];
return new FileStreamResult(file, "your file type goes here");
}
在视图中,将click事件添加到下载按钮
$("#yourBtnHere").click(function () {
DownloadFile();
});
创建DownLoadFile函数:
function DownloadFile()
{
$('#progressMsg').show();
$.ajax({
dataType: 'json',
type: 'POST',
url: "/YourController/SetupExport",
success: function (result) {
if (result.success) {
$('#progressMsg').hide();
window.open( "/YourController/DownloadExport" + "?fileName=" + result.fileName);
}
},
error: function () {
//show your error here;
}
});
}
这种方法的负面部分是我们必须两次调用控制器。
答案 2 :(得分:1)
您需要控制客户端的进度消息。
使用XHR(XMLHttpRequest)文件下载,您可以监控下载并显示进度条(如果需要)。或者使用更简单的方法来发送简单的消息,在发出下载请求之前将其打开,然后再将其关闭。
以下是:How to get progress from XMLHttpRequest。
适用于ASP.NET MVC的代码:
在您的控制器方法中,将Content-Length标头添加到Response对象:
public FileResult DownloadExport()
{
string fileName = "example";
// Add Content-Length header
FileInfo i = new FileInfo(fileName);
Response.AddHeader("Content-Length", i.Length.ToString());
return File(fileName, System.Net.Mime.MediaTypeNames.Application.Octet, Path.GetFileName(fileName));
}
然后,将提交按钮的onclick
事件连接到下面的sendreq()
函数。 updateProgress()
函数是XMLHttpRequest对象的onprogress
事件的处理程序:
function sendreq(evt)
{
var req = new XMLHttpRequest();
$('#progressbar').progressbar();
req.onprogress=updateProgress;
req.open('GET', 'Controller/DownloadExport', true);
req.onreadystatechange = function (aEvt) {
if (req.readyState == 4)
{
}
};
req.send();
}
function updateProgress(evt)
{
if (evt.lengthComputable)
{ //evt.loaded the bytes browser receive
//evt.total the total bytes seted by the header
//
var percentComplete = (evt.loaded / evt.total)*100;
$('#progressbar').progressbar( "option", "value", percentComplete );
}
}
编辑 - 使用消息代替进度条
<div>
<!-- Your other markup -->
<div id="progressMsg" style="display:none">Please wait...</div>
<button onclick="sendreq()">Submit</button>
</div>
<script>
function sendreq(evt)
{
var req = new XMLHttpRequest();
req.open('GET', 'Controller/DownloadExport', true);
req.onreadystatechange = function (aEvt) {
if (req.readyState == 4) {
//4 = complete
$('#progressMsg').hide();
}
};
$('#progressMsg').show();
req.send();
}
</script>
注意req.open()的第三个参数表明该调用是异步的。一旦调用完成,onreadystate事件处理程序就会隐藏消息。