【问题标题】:jqgrid + EF + MVC: How to export in excel? Which method you suggest?jqgrid + EF + MVC:如何在excel中导出?你建议哪种方法?
【发布时间】:2012-03-09 12:49:07
【问题描述】:

我正在使用 jqgrid(标准)和 EF 4 + MVC3。我想实现excel导出。你会建议我哪种方法?

要生成 excel,我想使用 Stephen Walther 博士的 this library,它具有三种类型的输出并允许定义标题。如果您认为它对我的目的有效,请告诉我。

我问这个问题是因为我还在接近实现 excel 导出并且我发现了几种技术。有些人建议进行 csv 导出,其他人则表示它应该返回 JSON 输出,我不清楚 jqgrid 的免费版本中是否存在此功能。无论如何,我想将数据传递给 Walther 的对象。

关于jqgrid的代码,我找到了Oleg的this interesting answer,但是不明白能不能应用到我的需要。

不幸的是,到目前为止,我只找到了使用 EF MVC 导出 excel 的部分解决方案,但没有解决方案或完整的示例......

关于 MVC 逻辑,我将按照@Tommy 的建议实现和开发this code

如果这个问题可能很愚蠢,请见谅,我只是一个(爱好者)初学者。

感谢您的宝贵帮助! 最好的问候

【问题讨论】:

    标签: asp.net-mvc excel jqgrid export


    【解决方案1】:

    我查看了斯蒂芬的帖子,它已经过时了,顺便说一句,这并没有弄错。 如果您不需要自定义格式、标题和样式,那么我认为使用 CSV,因为它非常简单。
    更重要的是,不要认为从内部使用 EF 进行数据访问的 MVC 站点导出 excel 比使用 ActiveRecord 的 Ruby on Rails 站点更难。对我来说,这是独立的关注点,出口不应该对底层技术(至少不是直接)产生任何新的影响,只是数据的结构,仅此而已。
    搜索允许执行 Excel 读/写和导出的 codeplex 库,这些天有很多,许多非常好的解决方案,由全球数千名开发人员定期维护和测试。如果我是你,我不会使用斯蒂芬解决方案,因为看起来他偶尔会在记事本中输入它然后粘贴到帖子中 - 没有单元测试,没有扩展点 + 它在 VB 中,所以更难理解,但可能是这就是我。 希望这能有所帮助并祝你好运

    【讨论】:

    • 感谢您的回复和建议。如果您查看博客,Stephen 的解决方案包括带有下载链接的 vb + c# 代码和现成的示例。无论如何,我想要一个完整的示例来描述如何使用 MVC 导出 jqgrid 的数据。直到现在,我还没有找到任何完整的东西,而且我仍然缺乏知识让我不仅不知道要使用什么库来导出,而且主要是如何将 jqgrid 数据传递给我的控制器。任何进一步的帮助都将是宝贵的。非常感谢!
    • 另外,我相信您的导出功能不应与 jqGrid 绑定
    【解决方案2】:

    正如我之前写的(参见 herehere 例如)将网格数据导出到 XML 的最佳方法是使用 Open XML SDK 2.0

    Stephen Walther 博士的post 展示了如何创建 Excel 可以读取的 HTML 文件。它不是 Excel 文件,仍需转换为 Excel 格式。 CSV 的使用有更多的问题。根据源表中的内容,自动转换为 Excel 数据类型可能是绝对错误的。在我为客户开发的一个项目中,网格包含有关软件产品的信息:产品名称、版本等。软件版本有时会显示为日期(例如 1.3.1963),并且此类单元格将被错误转换(在德语中,使用“.”作为日期中的分隔符)。结果,一个人遇到了非常棘手的问题。 CSV 中带有逗号的文本的使用也会经常被错误地导入。即使引用带有逗号 (,) 的单元格并转义了具有配额的文本,导入仍然是错误的,尤其是在第一列中。我不想在这里解释所有尝试和错误的整个历史,但毕竟我决定放弃使用 CSV 和 HTML 并开始使用 Open XML SDK 2.0,它允许创建 真正的 Excel 文件 扩展名为 XLSX。这种方式看起来很完美,因为不需要任何办公室 组件安装在服务器上,无需额外的许可证。

    唯一的限制是一个人应该能够使用DocumentFormat.OpenXml.dll,所以你的服务器程序应该在任何Windows操作系统上运行。众所周知,XLSX 文件是 ZIP 文件,其中包含一些 XML 文件。如果您仍然不知道,我建议您将 XLSX 文件重命名为 ZIP 文件并解压缩。 Open XML SDK 2.0 是与 XLSX 文件一起使用的库,就像使用 XML 文件一样。因此不需要额外的 Office 组件。

    可以找到很多关于如何使用 Open XML SDK 2.0 的信息(参见 hereherehere)。许多有用的代码示例可以直接在 MSDN 上找到(请参阅 here)。然而,至少在第一次实际使用 Open XML SDK 2.0 时并不那么容易。所以我根据自己使用的部分代码创建了一个演示。

    您可以从here下载演示项目。该演示是对the answerthis one 演示的扩展。

    为了导出数据,我使用了DataForExcel 辅助类。它有形式的构造函数

    DataForExcel(string[] headers, DataType[] colunmTypes, List<string[]> data,
                 string sheetName)
    

    或稍微简化的形式

    DataForExcel(string[] headers, List<string[]> data, string sheetName)
    

    也是唯一的公共方法

    CreateXlsxAndFillData(Stream stream)
    

    类创建Excel文件的用法如下

    var excelData = new DataForExcel (
        // column Header
        new[]{"Col1", "Col2", "Col3"},
        new[]{DataForExcel.DataType.String, DataForExcel.DataType.Integer,
              DataForExcel.DataType.String},
        new List<string[]> {
            new[] {"a", "1", "c1"},
            new[] {"a", "2", "c2"}
        },
        "Test Grid");
    Stream stream = new FileStream ("Test.xlsx", FileMode.Create);
    excelData.CreateXlsxAndFillData (stream);
    stream.Close();
    

    ASP.NET MVC 演示中的用法如下

    static readonly string[] HeadersQuestions = {
        "Id", "Votes", "Title"
    };
    static readonly DataForExcel.DataType[] ColunmTypesQuestions = {
        DataForExcel.DataType.Integer,
        DataForExcel.DataType.Integer,
        DataForExcel.DataType.String
    };
    
    public ActionResult ExportAllQuestionsToExcel () {
        var context = new HaackOverflowEntities ();
        var questions = context.Questions;
        questions.MergeOption = MergeOption.NoTracking; // we don't want to update the data
    
        // to be able to use ToString() below which is NOT exist in the LINQ to Entity
        // we should include in query only the properies which we will use below
        var query = questions.ToList ();
        if (query.Count == 0)
            return new EmptyResult ();
        var data = new List<string[]> (query.Count);
        data.AddRange (query.Select (item => new[] {
            item.Id.ToString(CultureInfo.InvariantCulture),
            item.Votes.ToString(CultureInfo.InvariantCulture),
            item.Title
        }));
    
        return new ExcelResult (HeadersQuestions, ColunmTypesQuestions, data,
                                "Questions.xlsx", "Questions");
    }
    

    ExcelResult 定义为

    public class ExcelResult : ActionResult {
        private readonly DataForExcel _data;
        private readonly string _fileName;
    
        public ExcelResult (string[] headers, List<string[]> data, string fileName, string sheetName) {
            _data = new DataForExcel (headers, data, sheetName);
            _fileName = fileName;
        }
    
        public ExcelResult (string[] headers, DataForExcel.DataType[] colunmTypes, List<string[]> data, string fileName, string sheetName) {
            _data = new DataForExcel (headers, colunmTypes, data, sheetName);
            _fileName = fileName;
        }
    
        public override void ExecuteResult (ControllerContext context) {
            var response = context.HttpContext.Response;
            response.ClearContent();
            response.ClearHeaders();
            response.Cache.SetMaxAge (new TimeSpan (0));
    
            using (var stream = new MemoryStream()) {
                _data.CreateXlsxAndFillData (stream);
    
                //Return it to the client - strFile has been updated, so return it. 
                response.AddHeader ("content-disposition", "attachment; filename=" + _fileName);
    
                // see http://filext.com/faq/office_mime_types.php
                response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                response.ContentEncoding = Encoding.UTF8;
                stream.WriteTo (response.OutputStream);
            }
            response.Flush();
            response.Close();
        }
    }
    

    为了使代码完整,我必须包含 DataForExcel 类的代码:

    public class DataForExcel {
        public enum DataType {
            String,
            Integer
        }
        private readonly string[] _headers;
        private readonly DataType[] _colunmTypes;
        private readonly List<string[]> _data;
        private readonly string _sheetName = "Grid1";
        private readonly SortedSet<string> _os = new SortedSet<string> ();
        private string[] _sharedStrings;
    
        private static string ConvertIntToColumnHeader(int index) {
            var sb = new StringBuilder ();
            while (index > 0) {
                if (index <= 'Z' - 'A') // index=0 -> 'A', 25 -> 'Z'
                    break;
                sb.Append (ConvertIntToColumnHeader (index / ('Z' - 'A' + 1) - 1));
                index = index % ('Z' - 'A' + 1);
            }
            sb.Append ((char)('A' + index));
            return sb.ToString ();
        }
    
        private static Row CreateRow(UInt32 index, IList<string> data) {
            var r = new Row { RowIndex = index };
            for (var i = 0; i < data.Count; i++)
                r.Append (new OpenXmlElement[] { CreateTextCell (ConvertIntToColumnHeader (i), index, data[i]) });
    
            return r;
        }
    
        private Row CreateRowWithSharedStrings(UInt32 index, IList<string> data) {
            var r = new Row { RowIndex = index };
            for (var i = 0; i < data.Count; i++)
                r.Append (new OpenXmlElement[] { CreateSharedTextCell (ConvertIntToColumnHeader (i), index, data[i]) });
    
            return r;
        }
    
        private Row CreateRowWithSharedStrings(UInt32 index, IList<string> data, IList<DataType> colunmTypes) {
            var r = new Row { RowIndex = index };
            for (var i = 0; i < data.Count; i++)
                if (colunmTypes != null && i < colunmTypes.Count && colunmTypes[i] == DataType.Integer)
                    r.Append (new OpenXmlElement[] { CreateNumberCell (ConvertIntToColumnHeader (i), index, data[i]) });
                else
                    r.Append (new OpenXmlElement[] { CreateSharedTextCell (ConvertIntToColumnHeader (i), index, data[i]) });
    
            return r;
        }
    
        private static Cell CreateTextCell(string header, UInt32 index, string text) {
            // create Cell with InlineString as a child, which has Text as a child
            return new Cell (new InlineString (new Text { Text = text })) {
                // Cell properties
                DataType = CellValues.InlineString,
                CellReference = header + index
            };
        }
    
        private Cell CreateSharedTextCell(string header, UInt32 index, string text) {
            for (var i=0; i<_sharedStrings.Length; i++) {
                if (String.Compare (_sharedStrings[i], text, StringComparison.Ordinal) == 0) {
                    return new Cell (new CellValue { Text = i.ToString (CultureInfo.InvariantCulture) }) {
                        // Cell properties
                        DataType = CellValues.SharedString,
                        CellReference = header + index
                    };
                }
            }
            // create Cell with InlineString as a child, which has Text as a child
            throw new InstanceNotFoundException();
        }
    
        private static Cell CreateNumberCell(string header, UInt32 index, string numberAsString) {
            // create Cell with CellValue as a child, which has Text as a child
            return new Cell (new CellValue { Text = numberAsString }) {
                // Cell properties
                CellReference = header + index
            };
        }
    
        private void FillSharedStringTable(IEnumerable<string> data) {
            foreach (var item in data)
                _os.Add (item);
        }
    
        private void FillSharedStringTable(IList<string> data, IList<DataType> colunmTypes) {
            for (var i = 0; i < data.Count; i++)
                if (colunmTypes == null || i >= colunmTypes.Count || colunmTypes[i] == DataType.String)
                    _os.Add (data[i]);
        }
    
        public DataForExcel(string[] headers, List<string[]> data, string sheetName) {
            _headers = headers;
            _data = data;
            _sheetName = sheetName;
        }
    
        public DataForExcel(string[] headers, DataType[] colunmTypes, List<string[]> data, string sheetName) {
            _headers = headers;
            _colunmTypes = colunmTypes;
            _data = data;
            _sheetName = sheetName;
        }
    
        private void FillSpreadsheetDocument(SpreadsheetDocument spreadsheetDocument) {
            // create and fill SheetData
            var sheetData = new SheetData ();
    
            // first row is the header
            sheetData.AppendChild (CreateRow (1, _headers));
    
            //const UInt32 iAutoFilter = 2;
            // skip next row (number 2) for the AutoFilter
            //var i = iAutoFilter + 1;
            UInt32 i = 2;
    
            // first of all collect all different strings in OrderedSet<string> _os
            foreach (var dataRow in _data)
                if (_colunmTypes != null)
                    FillSharedStringTable (dataRow, _colunmTypes);
                else
                    FillSharedStringTable (dataRow);
            _sharedStrings = _os.ToArray ();
    
            foreach (var dataRow in _data)
                sheetData.AppendChild (_colunmTypes != null
                                          ? CreateRowWithSharedStrings (i++, dataRow, _colunmTypes)
                                          : CreateRowWithSharedStrings (i++, dataRow));
    
            var sst = new SharedStringTable ();
            foreach (var text in _os)
                sst.AppendChild (new SharedStringItem (new Text (text)));
    
            // add empty workbook and worksheet to the SpreadsheetDocument
            var workbookPart = spreadsheetDocument.AddWorkbookPart ();
            var worksheetPart = workbookPart.AddNewPart<WorksheetPart> ();
    
            var shareStringPart = workbookPart.AddNewPart<SharedStringTablePart> ();
            shareStringPart.SharedStringTable = sst;
    
            shareStringPart.SharedStringTable.Save ();
    
            // add sheet data to Worksheet
            worksheetPart.Worksheet = new Worksheet (sheetData);
            worksheetPart.Worksheet.Save ();
    
            // fill workbook with the Worksheet
            spreadsheetDocument.WorkbookPart.Workbook = new Workbook (
                    new FileVersion { ApplicationName = "Microsoft Office Excel" },
                    new Sheets (
                        new Sheet {
                            Name = _sheetName,
                            SheetId = (UInt32Value)1U,
    
                            // generate the id for sheet
                            Id = workbookPart.GetIdOfPart (worksheetPart)
                        }
                    )
                );
            spreadsheetDocument.WorkbookPart.Workbook.Save ();
            spreadsheetDocument.Close ();
        }
    
        public void CreateXlsxAndFillData(Stream stream) {
            // Create workbook document
            using (var spreadsheetDocument = SpreadsheetDocument.Create (stream, SpreadsheetDocumentType.Workbook)) {
                FillSpreadsheetDocument (spreadsheetDocument);
            }
        }
    }
    

    以上代码直接创建新的 XLSX 文件。您可以扩展代码以支持更多数据类型,如我在代码中使用的StringInteger

    在您的应用程序的更专业版本中,您可以创建一些 XLSX 模板来导出不同的表。在代码中,您可以将数据放在单元格中,因此修改电子表格而不是创建。您可以创建完美格式的 XLSX 文件。 MSDN 中的示例(请参阅here)将帮助您在需要时实施该方式。

    更新The answer 包含更新的代码,允许生成带有更多单元格格式的 Excel 文档。

    【讨论】:

    • 非常感谢奥列格!你的回答总是透彻而有教育意义,非常感谢你所做的一切,你看到你投入了热情和奉献精神。不幸的是,我无法找到其他答案,对不起...但是您提供了更多宝贵的信息。只是好奇,如果可能的话...您写书了吗?谢了!
    • @Larry:不客气!很多年前我写过数学方面的书。最近我开始写一本关于jqGrid的书,但我仍然不确定我是否有足够的时间完成它。准备好足够的材料后,如果您愿意,我可以将第一章的 URL 发布给您。
    • @Larry:我在答案的代码中做了一些小的代码优化,也更新了the demo project。你可以重新加载它。
    • @RustinCohle:是的,当然。查看我在答案的 UPDATED 部分中引用的the answer。搜索“// 索引 2 - 灰色背景上没有图案文本”。它使用带有灰色背景的PatternFill。方法CreateColumnHeaderRow 设置行的StyleIndex。就是这样。
    • @RustinCohle:不客气!重要的是,不能从 JavaScript 代码启动 Excel,但如果响应包含 HTTP 标头 ContentType: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,Excel 将自动由 Web 浏览器启动。因此,它是由服务器直接生成 XLSX 的。从数据库中获取数据更加本地和更快(数据库通常具有快速的网络连接到 Web 服务器,但不是到每个 Web 客户端),因为从 jqGrid abd 将数据取消格式化并将其发送回 Web 服务器。
    猜你喜欢
    • 2012-03-15
    • 1970-01-01
    • 2010-09-07
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 2011-12-12
    相关资源
    最近更新 更多