当前位置: 首页 > news >正文

Excel数据导出小记二: [大数据示例]

文章目录

  • 前情
  • 流程
  • 数据查询实现
  • 数据导出实现

前情

.net8.0需从excel下载500MB以上的Excel。记录一下过程。
Excel数据导出小记[链接],之前记录了三种方式:
1、前端导出:远程服务器数据传输本地过慢
2、后端分页导出:SQLServer版本低分页麻烦,过于依赖建表、清表
3、后端批量导出:NOPI 导出
本文在记录在第三种情况下的小案例。

流程

导出流程分 业务流程、文件流程。

1、业务流程: 请求=> 数据查询=>查询结果(DataReader)=>workbook=>回收ODBC返回结果(查询成功正在下载)
2、文件流程: 查询结果(DataReader)=>workbook=> 保存服务器本地=>移动到共享文件服务器。

思路1、为什么流程分两个?

如果是一个线程,在保存完成之前,odbc的connect和前端request将一直被占用。
不如请求完了,异步保存。

思路2、为什么返回结果之前先将数据写入workbook?

因为 DataReader在 connect关闭后回收。所以在断开数据库之前,必须将数据写入内存,以供之后保存。

思路3、为什么要保存服务器本地?
API服务器(A)、文件服务器(B)
如新建到B,从workbook中写到excel,远程传输很慢,制约于A、B之间的网络正常。
而新建到A,再剪切到B,对网络依赖更低。更稳定。

思路4、为什么不直接保存到我的电脑

数据库、API服务器、文件服务器在同一网段,是局域网内的数据移动。
比api传输JSON,再本地写进EXCEL。效率快一倍有余。
存进共享文件服务器,也可降低同一工作的重复、协作文件传输频率。

数据查询实现

public static async Task<bool> OutPutExcel(SqlConnection connection, CommandType cmdType, List<CmdData> list, string extra)
{var workbook = new SXSSFWorkbook(null, 100, true); //NPOI  XSSF//定义路径tempPath 为api服务器临时路径   extra 为前端请求保存路径(文件服务器)string tempPath = @"C:\wwwroot\ExcelTemp\"; //需设置读写路径读写权限tempPath += Path.GetFileName(extra);//1、查询数据using (SqlCommand cmd = new SqlCommand(list[0].sql, connection)) {cmd.CommandType = cmdType;if (list[0].parameter != null) cmd.Parameters.AddRange(list[0].parameter);cmd.CommandTimeout = timeout; // 超时定义//1.1 转移数据 到 wookbookusing (DbDataReader reader = await cmd.ExecuteReaderAsync()){ExcelHelper.OutPut(reader, tempPath, ref workbook);}}//2、异常存储EXCEL_ = Task.Run(() => ExcelHelper.SaveExcel(tempPath, workbook, extra));//3、告知response,数据查询结束,保存正在进行return true;
}

1、 查询数据 ==> 2、结果存内存 ==> 3、告知前端在下载了,等会你看看结果。

数据导出实现

using NPOI.SS.UserModel;
using NPOI.XSSF.Streaming;
using System;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
public class ExcelHelper
{/// <summary>/// 1、DbDataReader=>workbook  /// </summary>public static  void OutPut(DbDataReader reader, string filePath, ref SXSSFWorkbook workbook){const int maxRowsPerSheet = 1_000_000;  // 每个Sheet最大行数 超过新建sheetconst int bufferSize = 50;            // 一次写入条数(优化内存)// 初始化流式工作簿(关键内存优化)try{int sheetIndex = 1;ISheet currentSheet = workbook.CreateSheet($"Sheet_{sheetIndex}");// 创建标题行IRow headerRow = currentSheet.CreateRow(0);for (int i = 0; i < reader.FieldCount; i++){headerRow.CreateCell(i).SetCellValue(reader.GetName(i));}int rowIndex = 1;  // 数据行从1开始(0是标题行)while (reader.Read()){// 达到最大行数时切换Sheetif (rowIndex >= maxRowsPerSheet){// 正确调用无参数FlushRows(NPOI 2.7.3+)((SXSSFSheet)currentSheet).FlushRows();  // 刷新当前Sheet[^1]sheetIndex++;currentSheet = workbook.CreateSheet($"Sheet_{sheetIndex}");rowIndex = 1;  // 新Sheet重置行索引}// 创建数据行IRow dataRow = currentSheet.CreateRow(rowIndex);// 写入所有列数据(类型安全处理)for (int col = 0; col < reader.FieldCount; col++){var cell = dataRow.CreateCell(col);// 根据数据类型安全写入if (reader.IsDBNull(col)){cell.SetCellValue((string)null);}else{//字符转换 switch (Type.GetTypeCode(reader.GetFieldType(col))){case TypeCode.String:cell.SetCellValue(reader.GetString(col));break;case TypeCode.DateTime:cell.SetCellValue(reader.GetDateTime(col));break;case TypeCode.Int16:cell.SetCellValue(reader.GetInt16(col));break;case TypeCode.Int32:cell.SetCellValue(reader.GetInt32(col));break;case TypeCode.Int64:cell.SetCellValue(reader.GetInt64(col));break;case TypeCode.Decimal:cell.SetCellValue((double)reader.GetDecimal(col));break;case TypeCode.Double:cell.SetCellValue(reader.GetDouble(col));break;case TypeCode.Single:cell.SetCellValue(reader.GetDouble(col));break;case TypeCode.Boolean:cell.SetCellValue(reader.GetBoolean(col));break;default:cell.SetCellValue(reader.GetValue(col).ToString());break;}}}if (rowIndex % 100000 == 0){_ = Task.Run(() => System.GC.Collect());}rowIndex++;}}catch (Exception ex){}}/// <summary>///2、保存 workbook=>Excel/// </summary>public static void SaveExcel(string tempPath, SXSSFWorkbook workbook, string truePath){try{//保存本地using (var fs = new FileStream(tempPath, FileMode.Create, FileAccess.Write)){workbook.Write(fs, false);}//异步执行剪切_ = Task.Run(() => ExcelHelper.CutFile(tempPath, truePath));}catch (Exception ex){}finally{workbook.Dispose();System.GC.Collect();}}/// <summary>///3、 剪切  本地 'a.xlsx'=>远程 '正在执行_a.xlsx'/// </summary>public static void CutFile(string fromPath, string truePath){//临时路径string tempPath=Path.GetDirectoryName(truePath)+"\\正在执行_"+ Path.GetFileName(truePath);// 检查源文件是否存在while (!File.Exists(tempPath)){Task.Delay(1000); // 减少CPU占用if (File.Exists(fromPath))File.Move(fromPath, tempPath);}//重命名while (File.Exists(tempPath)&&(!File.Exists(fromPath))){Task.Delay(1000); // 减少CPU占用ReName(tempPath, truePath);}}/// <summary>/// 4、重命名 '正在执行_a.xlsx'=>'a.xlsx'/// </summary>public static void ReName(string fromPath, string truePath){File.Move(fromPath, truePath);}
}

‘正在执行_’ 前缀,可识别,文件是否完成,以便打开。
1、保存到内存==> 2、保存到iis所在服务器 ==> 3、移动到共享服务器
==> 4、重命名 以表示 传输结束

http://www.xdnf.cn/news/1464175.html

相关文章:

  • JP4-7-MyLesson后台前端(一)
  • yolov8部署在一台无显卡的电脑上,实时性强方案
  • 【分享】基于百度脑图,并使用Vue二次开发的用例脑图编辑器组件
  • 探讨Xsens在人形机器人研发中的四个核心应用
  • 产线相机问题分析思路
  • 基于单片机的六足机器人控制系统设计
  • HTML文本格式化标签
  • 嵌入式解谜日志—多路I/O复用
  • AI日报 - 2025年09月05日
  • 专题:2025电力行业5G工厂及绿色转型、市场机制研究报告|附100+份报告PDF、数据仪表盘汇总下载
  • Ubuntu22.04-ROS2下navgation2编译到运行
  • 机器学习入门,用Lima在macOS免费搭建Docker环境,彻底解决镜像与收费难题!
  • 基于muduo库的图床云共享存储项目(五)
  • webshell及冰蝎双击无法打开?
  • 如何将视频从 iPhone 转移到 Mac
  • 开学信息收集不再愁,这个工具太省心
  • JavaEE---7.文件操作和IO
  • The Algorithmic Foundations of Differential Privacy - 3(2)
  • Windows Server2012 R2 安装.NET Framework 3.5
  • 安科瑞基站智慧运维云平台:安全管控与节能降耗双效赋能
  • python库 Py2app 的详细使用(将 Python 脚本变为 MacOS 独立软件包)
  • MacOS 15.6 编译SDL3 Android平台多架构so库
  • 【NVIDIA AIQ】自定义函数实践
  • windows安装flash-attn记录
  • 在 Java Web 项目中优雅地实现验证码拦截与校验
  • 新闻丨重庆两江新区党工委副书记、管委会主任许宏球一行莅临华院计算考察指导
  • Java 内存模型与垃圾回收机制详解
  • 迅为RK3568开发板OpenHarmonyv3.2-Beta4版本测试-命令终端
  • AI在目前会议直播系统中应用
  • CSS 选择器的优先级/层叠性