# 二十七、OpenXml/Excel 操作

## 什么是OpenXml

> \[来自百度百科]  Open XML 的标准化工作是由 Ecma International 通过其技术委员会 45 (TC45) 执行的，来自 Apple、Barclays Capital、BP、The British Library、Essilor、Intel、Microsoft、NextPage、Novell、Statoil、Toshiba 和 United States Library of Congress 的代表参与了该项工作。该标准旨在提供现有 ISO 标准所无法提供的独特好处，其中包括能够实现从现有二进制格式向基于 XML 的格式的高保真移植。

## OpenXml 官方文档

<https://github.com/OfficeDev/Open-XML-SDK>

## 如何使用

在 Hoa Framework 中，基于标准的 OpenXml/ClosedXML 开发的快捷Excel操作方式，可以大大减少Excel的复杂度以及大数据导出性能过高问题。

下面分为两个章节介绍 Excel 导入导出功能。

## 注入 IWorkbookContext 实例

首先在 `Hoa.Applicaiton.HoaApplicationModule.cs` 中注入 实例，如：

```csharp
builder.RegisterGeneric(typeof(WorkbookContext<>))
       .As(typeof(IWorkbookContext<>));
```

## 关于 IWorkbookContext 接口

`IWorkbookContext<T>/WorkbookContext<T>` 是Hoa Framework 框架独创的操作Excel上下文对象，该对象有三个方法，使用非常简单。

```csharp
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace Hoa.Workbook
{
    public interface IWorkbookContext<T> : IDisposable
    {
        // 导出并保存到文件中
        Task ExportToSaveAsync(string fileName, IEnumerable<T> datas);

        // 导出并保存到文件中（重载）
        Task ExportToSaveAsync(string fileName, params T[] datas);

        // 导出到内存流中
        Task<MemoryStream> ExportToMemoryStreamAsync(IEnumerable<T> datas);

        // 导出到内存流中（重载）
        Task<MemoryStream> ExportToMemoryStreamAsync(params T[] datas);

        // 导出到MVC的FileStreamResult中，可直接配合MVC下载
        Task<FileStreamResult> ExportToFileStreamResultAsync(string fileDownloadName, IEnumerable<T> datas);

        // 导出到MVC的FileStreamResult中，可直接配合MVC下载（重载）
        Task<FileStreamResult> ExportToFileStreamResultAsync(string fileDownloadName, params T[] datas);
    }
}
```

## Excel 导出

### 导出基本类型

![](/files/-MAEOQmUMcHv3t2cwAEL)

```csharp
using Hoa.Workbook;
using Hoa.Workbook.Enums;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;

namespace Hoa.Web.Host.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class HoaController : ControllerBase
    {
        private readonly IWorkbookContext<object> _workbookContext;

        public HoaController(
            IWorkbookContext<object> workbookContext
        {
            _workbookContext = workbookContext;
        }

        [HttpGet]
        [Route(nameof(ExportValueType))]
        public async Task ExportValueType()
        {
            await _workbookContext.ExportToSaveAsync("object.xlsx",
                1,  // int
                1d, // double
                10f,    // float
                1m, // decimal
                true,   // bool
                false,  // bool
                DateTime.Now,   // DateTime
                100000000000000000, // long
                1.9,    // number
                BorderStyle.DashDot, // enum
                "string type"   // string
                );
        }
    }
}
```

### 导出强类型

![](/files/-MAEQvNPxV5xgkrLIOyq)

```csharp
using Hoa.Workbook;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Hoa.Web.Host.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class HoaController : ControllerBase
    {
        private readonly IWorkbookContext<Person> _workbookContext;

        public HoaController(
            IWorkbookContext<Person> workbookContext)
        {
            _workbookContext = workbookContext;
        }

        [HttpGet]
        [Route(nameof(ExportValueType))]
        public async Task ExportValueType()
        {
            var persons = new List<Person>()
            {
                new Person(){Id=1,Name="Monk",Age=27,Address="广东省珠海市香洲区" },
                new Person(){Id=2,Name="Suncoder",Age=22,Address="广东省中山市石岐区" },
                new Person(){Id=3,Name="Cocoli",Age=30,Address="香港自治区" },
                new Person(){Id=4,Name="Steft",Age=45,Address="广西省安宁市某某乡镇" },
                new Person(){Id=5,Name="Dave",Age=35,Address="广东省珠海市南方软件园" },
                new Person(){Id=6,Name="Teyn",Age=20,Address="澳门经济特区" }
            };

            await _workbookContext.ExportToSaveAsync("persons", persons);

        }
    }

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public string Address { get; set; }
    }
}
```

### 更加丰富页面导出

![](/files/-MAEmxziWkETDPonaNRj)

```csharp
using Hoa.Workbook;
using Hoa.Workbook.Attributes;
using Hoa.Workbook.Enums;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Hoa.Web.Host.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class HoaController : ControllerBase
    {
        private readonly IWorkbookContext<Person> _workbookContext;

        public HoaController(
            IWorkbookContext<Person> workbookContext)
        {
            _workbookContext = workbookContext;
        }

        [HttpGet]
        [Route(nameof(ExportValueType))]
        public async Task ExportValueType()
        {
            var persons = new List<Person>()
            {
                new Person(){Id=1,Name="Monk",Age=27,Address="广东省珠海市香洲区",Photo=@"C:\images\bxs.jpg",Site="https://monksoul.gitbook.io/",PhoneNumber="13800138000" },
                new Person(){Id=2,Name="Suncoder",Age=22,Address="广东省中山市石岐区" ,Photo=@"C:\images\xtx.jpg",Site="https://www.baidu.com/",PhoneNumber="18676675647" },
                new Person(){Id=3,Name="Cocoli",Age=30,Address="香港自治区",Photo=@"C:\images\bxs.jpg",Site="https://www.google.com/",PhoneNumber="16609987544"  },
                new Person(){Id=4,Name="Steft",Age=45,Address="广西省安宁市某某乡镇",Photo=@"C:\images\xtx.jpg",Site="https://www.cnblogs.com/",PhoneNumber="13476437890"  },
                new Person(){Id=5,Name="Dave",Age=35,Address="广东省珠海市南方软件园",Photo=@"C:\images\bxs.jpg",Site="https://www.ithome.com/",PhoneNumber="16773467833"  },
                new Person(){Id=6,Name="Teyn",Age=20,Address="澳门经济特区",Photo=@"C:\images\xtx.jpg",Site="https://www.csdn.net/",PhoneNumber="18934568890" }
            };

            await _workbookContext.ExportToSaveAsync("persons", persons);
        }
    }

    [Excel("员工列表", "绩效列表")]
    [ExcelGroup("员工列表汇总", BackgroundColor = "#cccccc", Height = 30)]
    [ExcelHead(BackgroundColor = "#dedede", Height = 25)]
    [ExcelRow(-1, Height = 50, BackgroundColor = "#d9d9d9")]
    [ExcelRow(-2, Height = 50, BackgroundColor = "#f0f0f0")]
    public class Person
    {
        [ExcelCell("编号")]
        public int Id { get; set; }

        [ExcelCell("名称", FontBold = true, BackgroundColor = "#f5f5f5", FontColor = "#ff0000")]
        public string Name { get; set; }

        [ExcelCell("年龄", AlignmentHorizontal = Workbook.Enums.HorizontalAlignment.Right)]
        public int Age { get; set; }

        [ExcelCell("地址", Width = 30)]
        public string Address { get; set; }

        [ExcelCell("头像", CellType = Workbook.Enums.CellType.Image, ImageScale = 0.06, AlignmentHorizontal = HorizontalAlignment.Center, AlignmentVertical = VerticalAlignment.Center)]
        public string Photo { get; set; }

        [ExcelCell("个人主页", CellType = Workbook.Enums.CellType.Hyperlink, Tooltip = "这是是我个人主页", Width = 30)]
        public string Site { get; set; }

        [ExcelCell("联系方式", Format = "###,###,####", Width = 30, OutsideBorder = new object[] { BorderStyle.DashDot, "#0000ff" })]
        public string PhoneNumber { get; set; }
    }
}
```

### 导出并下载到本地

```csharp
using Hoa.Workbook;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace Hoa.Web.Host.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class HoaController : ControllerBase
    {
        private readonly IWorkbookContext<object> _workbookContext;
        public HoaController(IWorkbookContext<object> workbookContext)
        {
            _workbookContext = workbookContext;
        }

        [HttpGet]
        [Route(nameof(DownloadFile))]
        public async Task<IActionResult> DownloadFile()
        {
            var fileStreamResult = await _workbookContext.ExportToFileStreamResultAsync("test.xlsx", 1, 2, 3, 4, true, false);
            return fileStreamResult;
        }
    }
}
```

### 前端Angular下载示例

#### Get请求

只需要通过 `<a href='下载地址'>下载</a>` 即可。

#### Post请求

```javascript
// 导入HttpClient/HttpResponse 包
import { HttpClient, HttpResponse } from '@angular/common/http';

// 构造函数初始化
constructor(
    private httpClient: HttpClient
  ) { }

// 下载，第一个参数为post地址，第二个参数为post参数
this.httpClient.post('http://localhost:57310/api/Member/ExportMemberClaims', [{}], {
      responseType: 'blob',
      observe: 'events',
    }).subscribe((res: HttpResponse<Blob>) => {
      if (res.type !== 4) { return; }

      const objUrl = window.URL.createObjectURL(res.body);
      const a = document.createElement('a');
      a.href = objUrl;
      
      const fileName = 'excel.xlsx';  // 设置下载文件名
      a.download = decodeURIComponent(fileName);
      a.click();
      
      window.URL.revokeObjectURL(objUrl);
    });
```

### 更多功能

更多功能不断集成开发中，敬请期待。

## 特性说明

### \[Excel] 特性配置

配置Excel使用区域信息、样式。

* **`SheetNames`**：配置Excel多个Sheet
* **`Scale`**：配置每个Sheet数据比例，比如有两个Sheet，数据有1000条，则：`1:3`表示第一个Sheet有250条，第二个Sheet有750条。
* **`OutsideBorder`**：配置数据外边框样式，对象数组类型，数组第一个参数为 **`BorderStyle`** 枚举，第二个参数为 颜色值字符串
* **`InsideBorder`**：配置数据内边框样式，对象数组类型，数组第一个参数为 **`BorderStyle`** 枚举，第二个参数为 颜色值字符串
* **`AdjustToContents`**：是否自动调整内容宽度

### \[ExcelCell] 特性配置

配置单元格信息、样式。

* **`Name`**：配置列名，不配置采用属性名
* **`BackgroundColor`**：背景颜色
* **`CellType`**：单元格类型，有（Text：文本类型，Image：图片类型，HyperLink：链接类型）
* **`Hyperlink`**：链接值，如果等于 `@Value` 则取单元格的值，只有 CellType=HyperLink有效
* **`Tooltip`**：链接获取焦点提示，只有 CellType=HyperLink有效
* **`ImageScale`**：图片缩放比例，只有 CellType=Image有效
* **`AlignmentHorizontal`**：单元格数据水平对其方式
* **`AlignmentVertical`**：单元格数据垂直对其方式
* **`FontSize`**：字体大小
* **`FontBold`**：是否加粗
* **`FontColor`**：字体颜色
* **`Width`**：单元格宽度
* **`OutsideBorder`**：单元格外边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串
* **`InsideBorder`**：单元格内边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串
* **`Border`**：单元格四个方向边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串，第三个参数是方向 `BorderDirection`枚举类型
* **`SetToColumnStyle`**：是否将单元格样式设置给全局列
* **`Format`**：单元格内容格式，支持OpenXml和StringFormat格式

### \[ExcelGroup] 特性配置

配置分组单元格信息、样式。

* **`Name`**：配置列名，不配置采用属性名
* **`BackgroundColor`**：背景颜色
* **`CellType`**：单元格类型，有（Text：文本类型，Image：图片类型，HyperLink：链接类型）
* **`Hyperlink`**：链接值，如果等于 `@Value` 则取单元格的值，只有 CellType=HyperLink有效
* **`Tooltip`**：链接获取焦点提示，只有 CellType=HyperLink有效
* **`ImageScale`**：图片缩放比例，只有 CellType=Image有效
* **`AlignmentHorizontal`**：单元格数据水平对其方式
* **`AlignmentVertical`**：单元格数据垂直对其方式
* **`FontSize`**：字体大小
* **`FontBold`**：是否加粗
* **`FontColor`**：字体颜色
* **`Height`**：单元格高度
* **`OutsideBorder`**：单元格外边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串
* **`InsideBorder`**：单元格内边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串
* **`Border`**：单元格四个方向边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串，第三个参数是方向 `BorderDirection`枚举类型
* **`SetToColumnStyle`**：是否将单元格样式设置给全局列
* **`Format`**：单元格内容格式，支持OpenXml和StringFormat格式

### \[ExcelHead] 特性配置

配置表头信息、样式。

* **`BackgroundColor`**：背景颜色
* **`AlignmentHorizontal`**：单元格数据水平对其方式
* **`AlignmentVertical`**：单元格数据垂直对其方式
* **`FontSize`**：字体大小
* **`FontBold`**：是否加粗
* **`FontColor`**：字体颜色
* **`Height`**：单元格高度
* **`OutsideBorder`**：单元格外边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串
* **`InsideBorder`**：单元格内边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串
* **`Border`**：单元格四个方向边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串，第三个参数是方向 `BorderDirection`枚举类型
* **`Format`**：单元格内容格式，支持OpenXml和StringFormat格式

### \[ExcelRow] 特性配置

* **`Index`**：行索引，（0：不做任何行操作，> 0：特性行操作，-1/-2：奇偶行操作）
* **`Name`**：行的名字（**暂未启用该功能**）
* **`BackgroundColor`**：背景颜色
* **`AlignmentHorizontal`**：单元格数据水平对其方式
* **`AlignmentVertical`**：单元格数据垂直对其方式
* **`FontSize`**：字体大小
* **`FontBold`**：是否加粗
* **`FontColor`**：字体颜色
* **`Height`**：单元格高度
* **`OutsideBorder`**：单元格外边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串
* **`InsideBorder`**：单元格内边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串
* **`Border`**：单元格四个方向边框，对象数组类型，数组第一个参数为 `BorderStyle` 枚举，第二个参数为 颜色值字符串，第三个参数是方向 `BorderDirection`枚举类型

## Excel 导入


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://monksoul.gitbook.io/hoa/openxml_excel.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
