【问题标题】:Exporting Grid.MVC data to Excel将 Grid.MVC 数据导出到 Excel
【发布时间】:2022-03-29 20:39:09
【问题描述】:

我需要将 Grid.MVC 中的数据导出到 Excel。我使用了此链接中的解决方案。

http://www.codeproject.com/Articles/325103/MVC-Grid-to-Excel-file-download?msg=5161340#xx5161340xx

它正在工作,但我有 2 个问题。首先它在 chrome 中工作,但在 IE 中不工作。它在 IE 中给我一个错误(文件无法读取)。第二个问题是,当我过滤 Grid 时,Excel 中的导出数据仍然显示所有数据,而不是过滤后的数据。

如果这不是一个好的解决方案,请提供将 Grid.MVC 数据导出到 excel 的示例。

【问题讨论】:

  • 对于“文件无法读取”,这有点猜测,但从示例中我注意到 curContext.Response.AddHeader("content-disposition", "attachment;filename=" + fileName); 并记得 Firefox 有一些问题,如果文件名有空格,因此会混淆浏览器。您绝对应该将文件名放在引号中,如下所示:curContext.Response.AddHeader("Content-Disposition", string.Format("attachment;filename=\"{0}\"", fileName));
  • 该代码无法在任何浏览器中运行,因为它不会创建 Excel 文件。它创建一个带有假扩展名的 HTML 表。该HTML 标记 的任何问题,例如某些单元格中的意外< 都会破坏它。代码将运行,但没有程序能够导入该 HTML 文件
  • 很多 个重复的问题表明使用像 Epplus 这样的库在 C# 中创建一个真正的 xlsx 文件是多么容易。还有其他问题展示了如何使用适当的库在 Javascript 中执行相同的操作。

标签: c# asp.net-mvc asp.net-mvc-4


【解决方案1】:

这是一个服务器端解决方案

在这种情况下,客户端组件并不重要。您传递用于导出的任何数据,它都会将其导出到 excel 文件并下载。当我想要导出数据时,我使用最新的过滤器并检索显示的相同数据。

希望对你有帮助。

  • 我使用GridView 来显示我的数据。
  • 我还使用了一个简单的数据表。您可以从DataBase 或任何地方检索数据。
  • DataView 只是帮我过滤数据。
  • 您需要将ClosedXML 添加到您的项目中。使用这个NuGet 命令:Install-Package ClosedXML

aspx

<form id="form1" runat="server">
<div>
    <asp:TextBox ID="TextBox1" runat="server" placeholder="Name or Family" ></asp:TextBox>
    <asp:Button ID="ButtonFilter" runat="server" Text="Filter" OnClick="ButtonFilter_Click" />
    <br />
    <br />
    <asp:GridView ID="GridView1" runat="server">
        </asp:GridView>
    <br />
    <br />
    <asp:Button ID="ButtonExport" runat="server" Text="Export" OnClick="ButtonExport_Click" />
</div>
</form>

C# 代码

private DataTable dt = new DataTable();
private DataView dv;

private void Page_Load(object sender, System.EventArgs e)
{
    dt.Columns.Add("Id");
    dt.Columns.Add("EmployeeName");
    dt.Columns.Add("EmployeeFamily");

    for (int i = 0; i < 10; i++)
    {
        var r1 = dt.NewRow();
        r1["Id"] = i + 100;
        r1["EmployeeName"] = "Name " + i.ToString();
        r1["EmployeeFamily"] = "Family " + i.ToString();
        dt.Rows.Add(r1);
    }

    dv = new DataView(dt);

    GridView1.DataSource = dv;
    GridView1.DataBind();
}


private MemoryStream GetStream(XLWorkbook excelWorkbook)
{
    MemoryStream fs = new MemoryStream();
    excelWorkbook.SaveAs(fs);
    fs.Position = 0;
    return fs;
}


protected void ButtonFilter_Click(object sender, EventArgs e)
{
    dv.RowFilter = $"EmployeeName LIKE '%{TextBox1.Text}%' OR EmployeeFamily LIKE '%{TextBox1.Text}%'";

    GridView1.DataSource = dv;
    GridView1.DataBind();
}


protected void ButtonExport_Click(object sender, EventArgs e)
{
    dv = new DataView(dt);
    dv.RowFilter = $"EmployeeName LIKE '%{TextBox1.Text}%' OR EmployeeFamily LIKE '%{TextBox1.Text}%'";

    using (XLWorkbook wb = new XLWorkbook())
    {
        wb.Worksheets.Add(dv.ToTable(), "Employees");
        string myName = HttpContext.Current.Server.UrlEncode("Employees.xlsx");
        MemoryStream stream = GetStream(wb);
        HttpContext.Current.Response.Clear();
        HttpContext.Current.Response.Buffer = true;
        HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + myName);
        HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
        HttpContext.Current.Response.BinaryWrite(stream.ToArray());
        HttpContext.Current.Response.End();
    }
}





更新:GridMvc 版本

在这个版本中,我使用了GridMvc,但我仍然在服务器端制作 Excel 文件

ASPX

@model IList<GridMvcExportToExcel.Controllers.EmployeeModel>
@using GridMvc.Html

@{
    ViewBag.Title = "Home Page";
}

<script type="text/javascript">
    function exportToExcel() {
        debugger;
        var txtFilter = $('#txtFilter').val(); // get the textbox value
        var url = 'http://localhost:54312/Home/ExportToExcel?txtFilter=' + txtFilter;
        location.href = url; // redirect
        return false; // cancel default redirect
    };

</script>

<div>
    @using (Html.BeginForm("Index", "Home"))
    {
        @Html.TextBox("txtFilter", "", new { id = "txtFilter" })
        <button type="submit">Filter</button>
    }
    <br />

    @Html.Grid(Model).Columns(c =>
    {
        c.Add(x=>x.Id).Titled ("Employee Id");
        c.Add(x=>x.Name).Titled ("First Name").Filterable(false);
        c.Add(x=>x.Family).Titled ("Last Name").Filterable(true);
    }).WithPaging(50)

    <input type="button" id="exportToExcel" value="Export to Excel" onclick="exportToExcel()" />
</div>

C#

public class EmployeeModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Family { get; set; }
}

public class HomeController : Controller
{
    private IList<EmployeeModel> employees = new List<EmployeeModel>();

    public HomeController()
    {
        for (int i = 0; i < 20; i++)
        {
            employees.Add(new EmployeeModel()
            {
                Id = i + 1,
                Name = "Name " + (i + 1).ToString(),
                Family = "Family " + (i + 1).ToString(),
            });
        }
    }

    public ActionResult Index(string txtFilter)
    {
        txtFilter = txtFilter ?? "";
        var result = employees.Where(x => x.Name.Contains(txtFilter) || x.Family.Contains(txtFilter) || x.Id.ToString() == txtFilter);
        return View(result.ToList());
    }

    public void ExportToExcel(string txtFilter)
    {
        txtFilter = txtFilter ?? "";
        var result = employees.Where(x => x.Name.Contains(txtFilter) || x.Family.Contains(txtFilter) || x.Id.ToString() == txtFilter).ToList();

        DataTable table = new DataTable();
        using (var reader = ObjectReader.Create(result))
        {
            table.Load(reader);
        }

        using (XLWorkbook wb = new XLWorkbook())
        {
            wb.Worksheets.Add(table, "Employees");
            string myName = HttpContext.Server.UrlEncode("Employees.xlsx");
            MemoryStream stream = GetStream(wb);
            HttpContext.Response.Clear();
            HttpContext.Response.Buffer = true;
            HttpContext.Response.AddHeader("content-disposition", "attachment; filename=" + myName);
            HttpContext.Response.ContentType = "application/vnd.ms-excel";
            HttpContext.Response.BinaryWrite(stream.ToArray());
            HttpContext.Response.End();
        }
    }

    private MemoryStream GetStream(XLWorkbook excelWorkbook)
    {
        MemoryStream fs = new MemoryStream();
        excelWorkbook.SaveAs(fs);
        fs.Position = 0;
        return fs;
    }
}

【讨论】:

  • 不要使用字符串插值来创建 SQL 语句。如果有人在 TextBox1 中输入 '; drop table Employees; -- 会发生什么? xlsx 文件的内容类型是 application/vnd.openxmlformats-officedocument.spreadsheetml.sheetapplication/vnd.ms-excel 用于xls 文件
  • 当然,你是对的。我从来没有这样做。但这里是对数据表的简单过滤器。感谢您的通知。
  • @SaeidAmini 谢谢,但不是 grid.Mvc 包。
  • @xralf 数据是在服务器端过滤还是在客户端过滤?
  • @SaeidAmini 在客户端
【解决方案2】:

我有一个适合我的 javascript/jquery 解决方案。

当您使用 grid.mvc 时,它会向 thead 和 tbody 添加一些类,需要删除这些类才能在生成的 excel 文件上正确导出/可视化。我也在使用grid.mvc,这个代码导出到excel,如果这对你有用,请告诉我。

<script>

    $("#btnExport").click(function (e) {
        $('.grid-wrap').find('table').removeAttr('class');
        $('.grid-header').removeAttr('class');
        $('.grid-row').removeAttr('class');

        $('.grid-cell').removeAttr('data-name');
        $('.grid-cell').removeAttr('class');


        window.open('data:application/vnd.ms-excel,' + $('.grid-wrap').html());

       
       //MakeAnyFunctionToReloadThePageToGetTheClassesAgain();
        e.preventDefault();
    });

</script>
@Html.Grid(Model).Columns(columns =>
{
    
    columns.Add(foo => foo.Date).Sortable(true).Filterable(true);
    columns.Add(foo => foo.User).Sortable(true).Filterable(true);
    columns.Add(foo => foo.Controller).Sortable(true).Filterable(true);
    columns.Add(foo => foo.Action).Sortable(true).Filterable(true);
    columns.Add(foo => foo.ActionType).Sortable(true).Filterable(true);
    columns.Add(foo => foo.JsonObject).Sortable(true).Filterable(true);
}).WithMultipleFilters()

<button type="button" class="btn btn-danger" id="btnExport">export csv</button>

【讨论】:

  • 这不会生成 Excel 文件。打赌它会创建一个带有虚假扩展名的 HTML 表格,Excel 可能可能不能够导入和转换
【解决方案3】:

您必须从 url 中获取参数。 然后在后端构建自己的服务,获取参数并导出到excel

let params = new URLSearchParams(document.location.search);
let allParams = params.getAll('grid-filter');

【讨论】:

    【解决方案4】:

    在您的响应中设置以下标题,它将以正确的类型下载

    数据:应用程序/vnd.ms-excel

    【讨论】:

      【解决方案5】:

      我在这里聚会可能迟到了,但我找到了一个可以使用GridMvc 插件的内置filtering 选项的解决方案。此解决方案不需要任何客户端操作或应用自定义过滤器。这就是你的做法。

      安装插件时,它会生成一个名为 _Grid.cshtmlpartial 视图,该视图位于:~/Views/Shared/_Grid.cshtml 位置。

      现在我观察到,每次您在网格上调用事件时,都会调用此部分视图,然后我在流程中进行调试,发现有问题的 Model 具有不同的属性,例如 RenderOptionsItemsToDisplay 等.

      所以我发现过滤后的数据也存储但不直接存储在属性中。有一个名为:ItemsToDisplay 的属性,但不幸的是,如果您在网格上应用 paging,那么它将只存储第一个 n 项目,其中 n 是分页限制。

      最后我不得不求助于reflection 来获取整个过滤列表,这会影响网格的性能,但影响不大,它会完成工作。

      _Grid.cshtml:

      @using GridMvc.Columns
      @using System.Reflection;
      @model GridMvc.IGrid
      @{ 
          try
          {
              Type t = typeof(GridMvc.Html.GridHtmlOptions<MyModel>);
              FieldInfo[] fi = t.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
              foreach (FieldInfo info in fi)
              {
                  if (info.Name == "_source")
                  {
                      object _source = info.GetValue(Model);
                      PropertyInfo gridItemsProperty = _source.GetType().GetProperty("GridItems");
                      object gridItemsData = gridItemsProperty.GetValue(_source);
                      //Set the filtered list here in Session
                      Session["CurrentFilteredGridForExcelExport"] = (IEnumerable<MyModel>)(gridItemsData);
                  }
              }
          }
          catch
          {
      
          }
      }
      
      @if (Model.RenderOptions.RenderRowsOnly)
      {
          @RenderGridBody();
      }
      else
      {
          <div class="grid-mvc" data-lang="@Model.Language" data-gridname="@Model.RenderOptions.GridName" data-selectable="@Model.RenderOptions.Selectable.ToString().ToLower()" data-multiplefilters="@Model.RenderOptions.AllowMultipleFilters.ToString().ToLower()">
              <div class="grid-wrap">
                  <table class="table table-striped grid-table">
                      @* Draw grid header *@
                      <thead>
                          @RenderGridHeader()
                      </thead>
                      <tbody>
                          @RenderGridBody()
                          @RenderGridFooter()
                      </tbody>
                  </table>
                  @RenderGridFooter()
              </div>
          </div>
      }
      @helper RenderGridBody()
      {
          if (!Model.ItemsToDisplay.Any())
          {
              <tr class="grid-empty-text">
                  <td colspan="@Model.Columns.Count()">
                      @Model.EmptyGridText
                  </td>
              </tr>
          }
          else
          {
              foreach (object item in Model.ItemsToDisplay)
              {
                  <tr class="grid-row @Model.GetRowCssClasses(item)">
                      @foreach (IGridColumn column in Model.Columns)
                      {
                          @column.CellRenderer.Render(column, column.GetCell(item))
                      }
                  </tr>
              }
          }
      }
      @helper RenderGridHeader()
      {
          if (Model.RenderOptions.ShowGridItemsCount)
          {
                  <div class="grid-itemscount">
                      <span class="grid-itemscount-label">@Model.RenderOptions.GridCountDisplayName:</span>
                      <span class="grid-itemscount-caption">@Model.ItemsCount</span>
                  </div>
          }
      
          <tr>
              @foreach (IGridColumn column in Model.Columns)
              {
                  @column.HeaderRenderer.Render(column)
              }
          </tr>
      }
      @helper RenderGridFooter()
      {
          <div class="grid-footer">
              @if (Model.EnablePaging && Model.Pager != null)
              {
                  @Html.Partial(Model.Pager.TemplateName, Model.Pager)
              }
          </div>
      }
      

      最后,您可以像这样在Controller 端导出这个过滤后的网格:

      public void ExportToExcel()
      {
          List<MyModel> mymodel= new List<MyModel>();
          if(Session["CurrentFilteredGridForExcelExport"] != null)
          {
              var datasetFromSession = (IEnumerable<MyModel>)(Session["CurrentFilteredGridForExcelExport"]);
              mymodel = datasetFromSession.ToList();
          }
          
          //Use your export logic with this dataset   
      }
      

      希望这对仍在使用此网格插件的人有所帮助。干杯!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-09-11
        • 2016-06-30
        • 2017-03-27
        • 2010-10-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多