我有许多页面需要支持将数据导出到Excel电子表格。我可以很好地生成Excel文件,但我正在尝试弄清楚如何抽象这种行为,以便它可以从我需要的所有页面轻松地重用。我目前的想法是使用静态实用方法,如下所示:
public static void SendExcelFile(System.Web.UI.Page callingPage, string downloadFileName, List<List<string>> data, string worksheetTitle)
{
string tempFileName = Path.GetTempFileName();
try
{
// Generate file using ExcelPackage
GenerateExcelDoc(tempFileName, data, worksheetTitle);
callingPage.Response.AddHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
callingPage.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
callingPage.Response.AddHeader("Content-Length", new FileInfo(tempFileName).Length.ToString());
callingPage.Response.TransmitFile(tempFileName);
}
finally
{
//When this is removed, the method works as expected.
if (File.Exists(tempFileName))
File.Delete(tempFileName);
}
}
我正在调用SendExcelFile
的点击处理程序如下所示:
protected void lnkExport_Click(object sender, EventArgs e)
{
List<List<string>> dataList = GatherDataForSpreadsheet();
Utility.SendExcelFile(this, "fileNameForDownload.xlsx", dataList, "MyReports");
}
此代码可以很好地用作调用页面的实例方法。但是,作为静态方法,它根本不起作用。当我单击调用此按钮时,浏览器会无限期地显示加载动画,但从不提示文件下载。
我对ASP.NET(以及一般的网络编程)非常陌生,所以我确定我在这里遗漏了一些东西。有人可以解释一下我所看到的行为,并建议一种合理的替代方法吗?
编辑:如果我在结尾删除对File.Delete()的调用,该方法按预期工作。 Response.TransmitFile()是否异步进行传输?
编辑2:我只需要在删除文件之前调用Response.Flush()。请参阅下面的答案。 谢谢!
答案 0 :(得分:5)
问题是在发送数据之前删除了临时文件。我只需要像这样调用Response.Flush():
public static void SendExcelFile(System.Web.UI.Page callingPage, string downloadFileName, List<List<string>> data, string worksheetTitle)
{
string tempFileName = Path.GetTempFileName();
try
{
// Generate file using ExcelPackage
GenerateExcelDoc(tempFileName, data, worksheetTitle);
callingPage.Response.AddHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
callingPage.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
callingPage.Response.AddHeader("Content-Length", new FileInfo(tempFileName).Length.ToString());
callingPage.Response.TransmitFile(tempFileName);
callingPage.Response.Flush(); //This is what I needed
}
finally
{
if (File.Exists(tempFileName))
File.Delete(tempFileName);
}
}
答案 1 :(得分:1)
试试这个,您可以Request
直接关闭Response
和HttpContext.Current
:
public static void SendExcelFile(string downloadFileName, List<List<string>> data, string worksheetTitle)
{
var context = HttpContext.Current;
string tempFolder = context.Request.PhysicalApplicationPath + "temp";
string tempFileName = tempFolder + "tempFileName.xlsx"
if (!Directory.Exists(tempFolder))
Directory.CreateDirectory(tempFolder);
// Generate file using ExcelPackage
GenerateExcelDoc(tempFileName, data, worksheetTitle);
context.Response.AddHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
context.Response.AddHeader("Content-Length", new FileInfo(tempFileName).Length.ToString());
context.Response.TransmitFile(tempFileName);
File.Delete(tempFileName);
}
另一种选择是包含此方法的页面的基类,这可能是一个更容易的路径。您的页面不必从System.Web.UI.Page继承,它们可以继承其他内容,如下所示:
public class BasePage : System.Web.UI.Page
{
public void SendExcelFile(string downloadFileName, List<List<string>> data, string worksheetTitle)
{
string tempFolder =Request.PhysicalApplicationPath + "temp";
string tempFileName = tempFolder + "tempFileName.xlsx"
if (!Directory.Exists(tempFolder))
Directory.CreateDirectory(tempFolder);
// Generate file using ExcelPackage
GenerateExcelDoc(tempFileName, data, worksheetTitle);
Response.AddHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Length", new FileInfo(tempFileName).Length.ToString());
Response.TransmitFile(tempFileName);
File.Delete(tempFileName);
}
}
然后在您的页面中,该类看起来像:
public partial class MyPage : BasePage
{
//Stuff!
}
答案 2 :(得分:1)
我们需要更多信息 - 您正在做的事情应该有效。
我创建了一个精简版本,只是将调用页面的副本发送到客户端,它按预期工作:
public class Utility {
// This just sends the client a copy of the calling page itself
public static void SendExcelFile(Page callingPage) {
string path = callingPage.Request.PhysicalPath;
callingPage.Response.AddHeader("Content-Disposition", "attachment;filename=test.xls");
callingPage.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
callingPage.Response.AddHeader("Content-Length", new FileInfo(path).Length.ToString());
callingPage.Response.TransmitFile(path);
}
}
这是我的主叫页面:
public partial class main : System.Web.UI.Page {
protected void SendButton_Click(object sender, EventArgs e) {
Utility.SendExcelFile(this);
}
}
您认为与实施有什么不同吗?
答案 3 :(得分:1)
此时我将使用像Fiddler这样的HTTP调试代理来比较代码的工作(页面代码隐藏)和非工作(静态)版本生成的HTTP会话。
顺便说一句,您应该知道,如果多个用户同时单击该按钮,您编写的代码将无法正常工作 - 第一个用户的临时文件可能会被第二个用户的文件覆盖,并且第二个用户的文件可能会在传输过程中被删除!考虑在文件名中使用Path.GetTempFileName()
或guid来确保每个用户的文件都具有唯一名称。
答案 4 :(得分:0)
我会改用它。当前的HTTP上下文将在每个页面上提供。
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
HttpContext.Current.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
HttpContext.Current.Response.AddHeader("Content-Length", new FileInfo(tempFileName).Length.ToString());
HttpContext.Current.Response.TransmitFile(tempFileName);