NET - .NET Core WebAPI + Vue + Axios 导出Excel / CSV

这两种方法目前如果遇到0开头的数据,会默认把0去掉,可以考虑如果StartsWith(“0”),则在前面加个单引号(即’)
不推荐Excel导出,因为Excel2003一个Sheet最多导出65535条,Excel2007是10万4000多条,数据量大会报错,所以推荐使用CSV导出

 

  • 导出 Excel
/// <summary>
/// 将 DataTable 转换为 Excel
/// 需在 nuget 安装 DotNetCore.NPOI
/// </summary>
/// <param name="table">数据表</param>
/// <param name="sheetName">sheet名称</param>
/// <returns></returns>
public static byte[] DataTableToExcel(DataTable table, string title = null, string sheetName = "Sheet")
{
    try
    {
        IWorkbook workbook = new XSSFWorkbook();
        ISheet sheet = workbook.CreateSheet(sheetName);
        int cellsIndex = 0;
        // 标题
        if (!string.IsNullOrEmpty(title))
        {
            // 填充数据
            IRow cellsTitle = sheet.CreateRow(0);
            cellsTitle.CreateCell(0).SetCellValue(title);
            // 合并单元格
            sheet.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(0, 1, 0, table.Columns.Count - 1));
            cellsIndex = 2;
        }
        // 填充表头
        IRow cellsHeader = sheet.CreateRow(cellsIndex);
        for (int i = 0; i < table.Columns.Count; i++)
        {
            cellsHeader.CreateCell(i).SetCellValue(table.Columns[i].ColumnName);
        }
        // 填充数据
        cellsIndex += 1;
        foreach (DataRow dr in table.Rows)
        {
            IRow row = sheet.CreateRow(cellsIndex);
            for (int i = 0; i < table.Columns.Count; i++)
            {
                row.CreateCell(i).SetCellValue(StrHelper.ToString(dr[i]));
            }
            cellsIndex++;
        }
        byte[] buffer = null;
        using (MemoryStream ms = new MemoryStream())
        {
            workbook.Write(ms);
            buffer = ms.GetBuffer();
            ms.Close();
        }
        return buffer;
    }
    catch (Exception)
    {
        return null;
    }
}	

 

  • 导出 CSV
/// <summary>
/// 将 DataTable 转换为 CSV
/// </summary>
/// <param name="table">数据表</param>
/// <returns></returns>
public static Stream DataTableToCsv(DataTable table)
{
    try
    {
        StringBuilder builder = new StringBuilder();
        DataColumn column;
        int iColCount = table.Columns.Count;
        // 处理表头
        for (int i = 0; i < iColCount; i++)
        {
            if (i != 0) builder.Append(",");
            builder.Append("\"" + table.Columns[i].ColumnName + "\"");
        }
        builder.AppendLine();
        // 处理内容
        foreach (DataRow row in table.Rows)
        {
            for (int i = 0; i < iColCount; i++)
            {
                column = table.Columns[i];
                if (i != 0) builder.Append(",");
                if (Convert.IsDBNull(row[column])) builder.Append("\"\"");
                //else if (row[column].ToString().StartsWith("0")) builder.Append("\"'" + row[column].ToString() + "\"");
                else builder.Append("\"" + row[column].ToString() + "\"");
            }
            builder.AppendLine();
        }
        byte[] bytes = Encoding.GetEncoding("GB2312").GetBytes(builder.ToString());
        Stream stream = new MemoryStream(bytes);
        return stream ;
    }
    catch (Exception)
    {
        return null;
    }
}

 

  • .Net Core WebAPI Controller
/// <summary>
/// 导出 Excel
/// </summary>
/// <returns></returns>
[HttpGet("exportExcel")]
public async Task<IActionResult> ExportExcel()
{
    DataTable dataTable = new DataTable(); // 此处准备 dataTable 数据
    
    string name = "数据详情";
    byte[] bytes = Utils.DataTableToExcel(dataTable, name, name);
    return File(bytes, "application/octet-stream", $"{name}_{DateTime.Now:yyyyMMddHHmmssfff}.xlsx");
}

/// <summary>
/// 导出 CSV
/// </summary>
/// <returns></returns>
[HttpGet("exportCSV")]
public async Task<IActionResult> ExportCSV()
{
    DataTable dataTable = new DataTable(); // 此处准备 dataTable 数据

    System.IO.Stream stream = Utils.DataTableToCsv(dataTable);
    return File(stream, "application/octet-stream", $"数据详情_{DateTime.Now:yyyyMMddHHmmssfff}.csv");
}

 

  • .Net Core Startup.cs 配置

这里配置 .WithExposedHeaders("Content-Disposition") 是因为 Vue Axios response.headers 中默认获取不到 content-disposition 属性

启用跨域请求(CORS)

public void ConfigureServices(IServiceCollection services)
{
	// ......
	
    // 处理 api 请求跨域
    services.AddCors(options =>
    {
        options.AddPolicy("cors",
            builder =>
            {
                builder.AllowAnyMethod()
                .AllowAnyOrigin()
                .AllowAnyHeader()
                .WithExposedHeaders("Content-Disposition");
            });
    });

	// ......
}

 

  • Vue Axios
service.interceptors.response.use(
  response => {
    // blob 直接返回文件流数据
    if (response.config.responseType === 'blob') {
      console.log(response.headers);
      const fileName = decodeURI(
      	// response.headers['content-disposition'] 默认获取不到,需要在 .Net Core Startup.cs 中进行配置
        response.headers['content-disposition'].split('filename*=UTF-8\'\'')[1]
      )
      return Promise.resolve({ data: response.data, fileName: fileName })
    }

    // ......
  },
  error => {
    return Promise.reject(error)
  }
)

 

  • Vue 请求接口
export function exportCSV(param) {
    return request({
        url: '/controller/exportCSV',
        method: 'get',
        params: { ...param },
        responseType: 'blob'
    })
}

 

  • Vue 下载实现
export default {
  data() {
    return {
      listQuery: {},
    };
  },
  created() {},
  methods: {
    // 导出报表
    onExport() {
      exportCSV(this.listQuery).then((response) => {
        let blob = new Blob([response.data], { type: response.data.type });
        // 针对于 IE 浏览器的处理, 因部分 IE 浏览器不支持 createObjectURL
        if (window.navigator && window.navigator.msSaveOrOpenBlob) {
          window.navigator.msSaveOrOpenBlob(blob, response.fileName);
        } else {
          var downloadElement = document.createElement("a");
          var href = window.URL.createObjectURL(blob); // 创建下载的链接
          downloadElement.href = href;
          downloadElement.download = response.fileName; // 下载后文件名
          document.body.appendChild(downloadElement);
          downloadElement.click(); // 点击下载
          document.body.removeChild(downloadElement); // 下载完成移除元素
          window.URL.revokeObjectURL(href); // 释放掉 blob 对象
        }
      });
    },
  },
};

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于使用 Vite + Vue3 + TypeScript + Pinia + Vue Router + Axios + SCSS 并自动导入 API 的设置,你可以按照以下步骤进行操作: 1. 首先,确保你已经安装了 Node.js,并且版本大于等于 12.0.0。 2. 创建一个新的 Vue 项目,可以使用 Vue CLI 或者手动创建一个空文件夹。 3. 在项目根目录下,打开终端并执行以下命令安装 Vite: ```bash npm init vite@latest ``` 按照提示选择你的项目配置,包括选择 Vue 3、TypeScript 和其他选项。 4. 进入项目目录并安装依赖: ```bash cd your-project-name npm install ``` 5. 安装 Pinia 插件: ```bash npm install pinia ``` 6. 创建一个 `src/store` 目录,并在其中创建 `index.ts` 文件,用于定义和导出你的 Pinia store。 ```typescript // src/store/index.ts import { createPinia } from 'pinia' export const store = createPinia() // 可以在这里定义你的 store 模块 ``` 7. 在项目根目录下创建 `src/api` 目录,用于存放 API 请求相关的文件。 8. 在 `src/api` 目录下创建一个 `index.ts` 文件,用于自动导入所有 API 文件。 ```typescript // src/api/index.ts const modules = import.meta.globEager('./*.ts') const apis: any = {} for (const path in modules) { if (path !== './index.ts') { const moduleName = path.replace(/^.\/|\.ts$/g, '') apis[moduleName] = modules[path].default } } export default apis ``` 这样,你就可以在 `src/api` 目录下创建各种 API 请求的文件,例如 `user.ts`: ```typescript // src/api/user.ts import axios from 'axios' export function getUser(id: number) { return axios.get(`/api/user/${id}`) } ``` 然后,在你的组件中使用自动导入的 API: ```typescript import { defineComponent, ref } from 'vue' import { useUserStore } from '@/store' import apis from '@/api' export default defineComponent({ setup() { const userStore = useUserStore() const userId = ref(1) const fetchUser = async () => { const response = await apis.user.getUser(userId.value) userStore.setUser(response.data) } return { userId, fetchUser, } }, }) ``` 以上就是使用 Vite + Vue3 + TypeScript + Pinia + Vue Router + Axios + SCSS 并自动导入 API 的基本设置。你可以根据自己的需求进一步配置和扩展。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值