使用EPPlus生成Excel文件失败

15

当我使用EPPlus生成Excel文件时,Excel会给我以下错误消息:

Excel无法打开文件'myfilename.xlsx',因为文件格式或文件扩展名无效。请确认文件未损坏并且文件扩展名与文件格式相符。

这是我的代码:

public ActionResult Index()
{
    using (ExcelPackage package = new ExcelPackage())
    {
        // I populate the worksheet here.  I'm 90% sure this is fine
        // because the stream file size changes based on what I pass to it.

        var stream = new MemoryStream();
        package.SaveAs(stream);

        string fileName = "myfilename.xlsx";
        string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

        var cd = new System.Net.Mime.ContentDisposition
        {
            Inline = false,
            FileName = fileName
        };
        Response.AppendHeader("Content-Disposition", cd.ToString());
        return File(stream, contentType, fileName);
    }
}

有什么想法我做错了吗?


错误提示为 myfilename.xslx,而您的代码中显示的是 myfilename.xlsx。您真正使用的是哪一个? - M.Babcock
昨天已经回答了这个问题(https://dev59.com/QGHVa4cB1Zd3GeqPkTKe#9574414)。我认为这是同一个问题。 - Tim Schmelter
@M.Babcock - xlsx,那是个打错字。我会编辑问题的。 - Matt Grande
1
我发现当我将流位置设置为0时,这段代码可以正常工作,并且我发现它比被接受的解决方案更加简洁。 - stimms
3个回答

31

你只需要重置流的位置即可:stream.Position = 0;

不应该直接写入响应Response,这不是MVC的方式。它不遵循正确的MVC管道,并且将你的控制器动作代码与响应对象紧密耦合。

当你将文件名作为File()的第三个参数添加时,MVC会自动添加正确的Content-Disposition头...,所以你不需要手动添加。

简而言之,这就是你想要的:

public ActionResult Index()
{
    using (ExcelPackage package = new ExcelPackage())
    {
        // I populate the worksheet here.  I'm 90% sure this is fine
        // because the stream file size changes based on what I pass to it.

        var stream = new MemoryStream();
        package.SaveAs(stream);

        string fileName = "myfilename.xlsx";
        string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

        stream.Position = 0;
        return File(stream, contentType, fileName);
    }
}

关于这个问题,我正在使用类似的方法,不过目前我返回的是 FileStreamResponse 而不是文件对象。然而我遇到的问题是,如果用户在第一个文件正在被创建时发起多个请求,它们都会被锁住,导致 saveas 方法永远无法完成。您在使用 EPPlus 时遇到过这个问题吗? - Shawn
你能发布一个用于处理返回结果的ajax success方法吗? - Christian Doulos

10

你的代码没有展示将stream写入HttpResponse的操作——可能是在你未贴出来的File方法中完成的。

下面是一种可行的方式:

Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader(
            "content-disposition", String.Format(CultureInfo.InvariantCulture, "attachment; filename={0}", fileName));
Response.BinaryWrite(package.GetAsByteArray());
Response.End();

File 方法是 ASP.Net MVC 的一部分。我会尝试这种方式! - Matt Grande
奇怪,现在它说“Excel 在 'myfilename.xlsx' 中发现了无法读取的内容。您要恢复此工作簿的内容吗?如果您信任此工作簿的来源,请单击是。” 如果我点击是,似乎可以工作,但我不知道是什么导致了这个错误。 - Matt Grande
我之前见过这个错误信息,如果我没记错的话,它发生在省略了 Response.Clear() 的时候。另一个可能性是 EPPlus 的 bug 导致文件中存在无效数据。你可以通过在服务器上保存文件并查看是否在打开它时出现相同的错误来测试它是哪种情况。 - Joe
同时,当使用File方法时,您可能需要先将MemoryStream定位到流的开头(stream.Position = 0)。 - Joe
啊,"Excel 发现无法读取的内容" 是我的错。我试图在第 0 列设置宽度。 - Matt Grande
-1 这不是你应该这样做的方式...它不符合MVC的方式,而且会将你的代码与响应对象紧密耦合。你只需要重置流的位置即可 - Charlino

2

与Joe的答案类似,我仍然需要调用Response.ClearHeaders()

   protected void btnDownload_Click(object sender, EventArgs e)
   {

       ExcelPackage pck = new ExcelPackage();
       var ws = pck.Workbook.Worksheets.Add("Sample2");

       ws.Cells["A1"].Value = "Sample 2";
       ws.Cells["A1"].Style.Font.Bold = true;
       var shape = ws.Drawings.AddShape("Shape1", eShapeStyle.Rect);
       shape.SetPosition(50, 200);
       shape.SetSize(200, 100);
       shape.Text = "Sample 2 outputs the sheet using the Response.BinaryWrite method";
       Response.Clear();    
       Response.ClearHeaders();
       Response.BinaryWrite(pck.GetAsByteArray());
       Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
       Response.AddHeader("content-disposition", "attachment;  filename=Sample2.xlsx");
       Response.End();
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接