如何将数据导出到Excel?

6
我遵循这个指南,它是在另一篇帖子中推荐的,但我无法使其正常工作。当尝试重定向到/LogModelsController/ExportData时,我会收到404错误,而我理解应该这样做。

_LogPartialLayout.cshtml

@using (Html.BeginForm("ExportData", "LogModelsController", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    <table class="table">
        <tr>
            <th>
                @Html.ActionLink("message", "Index", new { sortOrder = ViewBag.NameSortParm, currentFilter = ViewBag.CurrentFilter })

            </th>
            <th>
                @Html.ActionLink("timestamp", "Index", new { sortOrder = ViewBag.NameSortParm, currentFilter = ViewBag.CurrentFilter })
            </th>
            <th>
                @Html.ActionLink("level", "Index", new { sortOrder = ViewBag.NameSortParm, currentFilter = ViewBag.CurrentFilter })
            </th>

        </tr>

        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.message)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.timeStamp)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.level)
                </td>
            </tr>
        }
    </table>
    <input type="submit" name="Export" id="Export" value="Export" />
}

_LogPartialLayout.cshtml

LogModelsController.cs

public ActionResult ExportData()
        {
            GridView gv = new GridView();
            gv.DataSource = db.Log.ToList();
            gv.DataBind();
            Response.ClearContent();
            Response.Buffer = true;
            Response.AddHeader("content-disposition", "attachment; filename=Loglist.xls");
            Response.ContentType = "application/ms-excel";
            Response.Charset = "";
            StringWriter sw = new StringWriter();
            HtmlTextWriter htw = new HtmlTextWriter(sw);
            gv.RenderControl(htw);
            Response.Output.Write(sw.ToString());
            Response.Flush();
            Response.End();

            return RedirectToAction("~/LogModels/Index");
        }

LogModel.cs

namespace ASDMVC.Models
{
    [Table("Log")]
    public class LogModel
    {
        [Key]
        public long id { get; set; }
        public string message { get; set; }
        public DateTime timeStamp { get; set; }
        public string level { get; set; }
        public int customerId { get; set; }
    }

    public class LogDBContext:DbContext
    {
        public LogDBContext() : base("MySqlConnection")
        {

        }

        public DbSet <LogModel> Log { get; set; }

    }
}

简而言之:我该如何正确地将这个表格导出到Excel?

附加问题:它是否也适用于PagedList?


编辑3 - 已解决

Index.cshtml:

@using (Html.BeginForm("ExportData", "LogModels"))
{
    <input type="submit" name="Export" id="Export" value="Export" />
}

LogModelsController.cs

public ActionResult ExportData()
{
    DataTable dataTable = GetData();

    using (ExcelPackage package = new ExcelPackage())
    {
        var ws = package.Workbook.Worksheets.Add("My Sheet");
        //true generates headers
        ws.Cells["A1"].LoadFromDataTable(dataTable, true);

        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);
    }
}

public DataTable GetData()
{
    DataTable dt = new DataTable();
    if (ModelState.IsValid)
    {
        using (SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MySqlConnection"].ConnectionString))
        {
            using (SqlCommand comm = conn.CreateCommand())
            {
                comm.Parameters.AddWithValue("@val1", Session["myID"]);
                comm.CommandText = "SELECT * FROM dbo.Log WHERE CustomerId = @val1";
                try
                {
                    conn.Open();
                    dt.Load(comm.ExecuteReader());
                }
                catch (SqlException e)
                {
                    throw new Exception(e.ToString());
                }
            }
        }
    }
    return dt;
}

我在CommandText中犯了一个错误,将"Id"写成了"CustomerId" - 现在Excel文件返回了整个表格。


1
该指南不会生成Excel文件。使用像EPPlus这样的库来创建一个真正的 Excel文件。现在,您正在发送伪装成旧式(二进制)Excel文件的HTML表格。Excel将尝试使用默认设置导入此文件。 - Panagiotis Kanavos
最好使用EPPLUS将文件导出为Excel文件。 - temUser
Html.BeginForm("ExportData", "LogModels"),从路由中删除“controller”部分。 - Cristi Pufu
@CristiPufu 当我更改这个时,我实际上得到了一个Excel文件!太棒了!但它只包含表格的第一行。我会更新帖子 :) - Danieboy
3个回答

4
该指南并未展示如何创建Excel文件,而是展示了一种通过创建包含表格的HTML文件的方法。Excel会尝试使用默认设置导入csv或包含表格的HTML文件,但往往会失败。
使用类似EPPlus这样的库来创建一个正确的Excel文件要容易得多。 我相信这个问题以前已经有答案了,但我找不到一个同时展示如何从数据中创建Excel文件和设置xlsx文件的正确内容类型的答案。
EPPlus提供了LoadFromDataTable和LoadFromCollection方法,可使用从DataTable或通用集合中提取的数据填充Excel范围。列或属性名称分别用作标题。
public ActionResult ExportData()
{

    //Somehow, load data to a DataTable

    using (ExcelPackage package = new ExcelPackage())
    {
        var ws = package.Workbook.Worksheets.Add("My Sheet");
        //true generates headers
        ws.Cells["A1"].LoadFromDataTable(dataTable, true);

        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);
    }
}

LoadFromDataTable或LoadFromCollection返回一个Excel单元格范围,可用于格式化表格:

var range=ws.Cells["A1"].LoadFromDataTable(table);
range.Style.Numberformat.Format = "#,##0.00";
range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Right;

您也可以将表格样式应用于范围,或将所需样式传递给LoadFromDataTableLoadFromCollection,例如:
ws.Cells[1,1].LoadFromDataTable(table, true, TableStyles.Light1);

该图书馆的Githup wiki展示了如何格式化结果、生成公式、数据透视表、处理表格等。

如果您想创建一个相对较大的工作表,将其保存到MemoryStream可能会成为一个问题。将相同的数据写入两次,一次写入MemoryStream,然后将其复制到输出中,会浪费循环。但是,从MVC操作直接写入输出流是不好的实践。诀窍是创建一个自定义的FileResult来处理EPPlus包,并返回它,而不是File返回的FileResult,例如:

public class EpplusResult:FileResult
{
    public EpplusResult(ExcelPackage package)
        : base("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
    {
        if (package == null)
        {
            throw new ArgumentNullException("package");
        }

        Package = package;
    }

    public ExcelPackage Package { get; private set; }

    protected override void WriteFile(HttpResponseBase response)
    {
        // grab chunks of data and write to the output stream
        Stream outputStream = response.OutputStream;
        using (Package)
        {
            Package.SaveAs(outputStream);
        }
    }
}

这允许您编写以下操作:

public FileResult ExportData()
{

    ExcelPackage package = new ExcelPackage();
    var ws = package.Workbook.Worksheets.Add("My Sheet");       
    ...
    ws.Cells[1,1].LoadFromDataTable(table, true, TableStyles.Light1);

    return new EpplusResult(package){FileDownloadName = "SomeFile.xlsx"};
}

那我会尝试一下,然后尽快回报 :) - Danieboy
我已经更新了帖子,以反映您建议后所做的更改,我对此还比较新,感谢您的耐心。 - Danieboy

2
不要创建基于HTML的Excel文件,它们会有很多问题。相反,使用像epplus这样的紧凑库。
public ActionResult ExportData()
{

    using (ExcelPackage package = new ExcelPackage())
    {
        var ws = package.Workbook.Worksheets.Add("LogMessages");
       //Headers
        ws.Cells["A1"].Value = "Message";
        ws.Cells["B1"].Value = "TimeStamp";
        ws.Cells["C1"].Value = "Level";


       var rowNumber=1;

        foreach (var log in DbContext.Log)
       {
           ws.Cells[rowNumber, 1].Value = vehicle.message;
           ws.Cells[rowNumber, 2].Value = vehicle.timeStamp;
           ws.Cells[rowNumber, 3].Value = vehicle.level;
           rowNumber++;
       }


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

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

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

我使用了Panagiotis Kanavos的代码结构来实现这个解决方案。

这个答案有问题。OP正在创建HTML文件,而不是“基于XML”的文件。EPPlus创建的是XLSX文件,它们压缩的XML。 - Panagiotis Kanavos
我已经编辑了我的答案,我知道它并没有真正回答这个问题,但是我以前使用HTML创建Excel文件时遇到了很多单元格格式、类型、颜色等方面的问题。 - Menelaos Vergis
这篇文章有点混乱。实际上,通过调用LoadFromDataTableLoadFromCollection,可以更轻松地从集合或DataTable创建一个工作表,就像这个SO问题中所示。OP还应该设置正确的内容类型,就像这个SO问题中所示。如果可能的话,这个问题可以被标记为这两个问题的重复。 - Panagiotis Kanavos
我会尽快查看所有这些链接! - Danieboy
我已经查看了所有这些链接,但似乎并不适用于我的问题,如果我理解正确的话,需要对代码进行一些重大更改才能使其工作。 - Danieboy
显示剩余2条评论

0

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