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

goFrame框架中如何实现文件的excel导出

需求场景

公司业务有一个excel导出的需求,数据量10w+,框架是goFrame。对一些需要处理的数据根据字典进行转化输出为string类型的值,然后转成excel表格。但是批量导出的数据又是很多条,我的实现采用了:协程、缓存、bytes、锁等进行实现的。

代码实现

logic的文件下的某个文件:logic/test.go

type sTest struct{}var (exportCache  = make(map[string]*mid.ExportCacheItem)cacheLock    sync.RWMutexcols = dao.Test.Columns()
)// 导出函数处理 req:前端传递主键id []int
func (*sAccountEvent) ExportData(ctx context.Context, req *v2.EventExportReq) (err error) {longCtx, cancel := context.WithTimeout(context.Background(), 1*time.Hour)defer cancel()// 缓存 Key:基于 req.Ids 排序后的哈希生成sort.Slice(req.Ids, func(i, j int) bool { return req.Ids[i] < req.Ids[j] })cacheKey := fmt.Sprintf("export:%x", md5.Sum([]byte(fmt.Sprintf("%v", req.Ids))))// 尝试读取缓存var allDataCache []*mid.TestEventResponsevar found boolif allDataCache, found = GetExportCache(cacheKey); !found {// 缓存未命中,分批次查询并构建缓存const batchSize = 1000allDataCache = make([]*mid.TestEventResponse, 0)for i := 0; i < len(req.Ids); i += batchSize {start := iend := i + batchSizeif end > len(req.Ids) {end = len(req.Ids)}// 构建子 ID 切片subIds := req.Ids[start:end]// 查询当前批次数据var batchData []*mid.TestEventResponseerr = dao.AccountEvent.Ctx(longCtx).WhereIn(cols.Id, subIds).Scan(&batchData)if err != nil {return gerror.Wrapf(err, "分批查询失败(%d-%d)", start, end)}allDataCache = append(allDataCache, batchData...)}SetExportCache(cacheKey, allDataCache)}// 构建 Excel 数据excelData := make([]*mid.TestEventExcel, 0, len(allDataCache))for _, event := range allDataCache {// 根据公司业务的字典翻译处理event.testExtraData = translateExtraDataHandler(event.OtherData)excelData = append(excelData, convertToExcelStruct(event))}// 构建 Excel 文件fileBytes, err := buildExcelBytes(excelData)if err != nil {return gerror.Wrap(err, "生成Excel失败")}// 设置下载响应头response := g.RequestFromCtx(ctx).Responseresponse.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8")response.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s","data.xlsx",))// 直接返回字节流response.Write(fileBytes)return
}// 构建bytes返回给前端处理
func buildExcelBytes(data []*mid.TestEventExcel) ([]byte, error) {f := excelize.NewFile()sheet := "Sheet1"index, _ := f.NewSheet(sheet)headers := []string{"主键", "Key值", "Value值",}for rowNum, item := range data {row := rowNum + 2_ = f.SetCellValue(sheet, fmt.Sprintf("A%d", row), item.Id)_ = f.SetCellValue(sheet, fmt.Sprintf("B%d", row), item.Key)_ = f.SetCellValue(sheet, fmt.Sprintf("C%d", row), item.Value)// 其他需要设置的字段}f.SetActiveSheet(index)// 返回字节流buff, err := f.WriteToBuffer()if err != nil {return nil, err}return buff.Bytes(), nil
}// convertToExcelStruct 转换为Excel结构
func convertToExcelStruct(event *mid.AccountEventResponse) *mid.TestEventExcel{return &mid.TestEventExcel{Id:                   event.Id,Key:            event.Key,Value:         event.Value,// ..........}
}// SetExportCache 缓存原始数据
func SetExportCache(key string, data []*mid.TestEventResponse) {cacheLock.Lock()defer cacheLock.Unlock()exportCache[key] = &mid.ExportCacheItem{Data:      data,ExpiresAt: time.Now().Add(10 * time.Minute),}
}// GetExportCache 获取缓存数据
func GetExportCache(key string) ([]*mid.TestEventResponse, bool) {cacheLock.RLock()defer cacheLock.RUnlock()item, ok := exportCache[key]if !ok || item.ExpiresAt.Before(time.Now()) {return nil, false}return item.Data, true
}

为什么不本地保存返回URL?

根据需求的,要自己本地保留,文件的命名可以进行自定义,所以没有使用f.SaveAs(“fileName.csv”)进行实现,上述这样做也是比较灵活,本来打算使用第三方库做,但是感觉数据量太大,后续使用协程,设置了最大数进行处理大约30w条数的一次性导出,这里就不做展示代码的实现了

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

相关文章:

  • Spring Boot快速开发:从零开始搭建一个企业级应用
  • 普通IT的股票交易成长史--20250509 缺口(1)
  • LeetCode难题解析:数字字符串的平衡排列数目
  • 阻焊工艺如何保障多层PCB可靠性?5大核心功能与工艺控制要点
  • 深入理解 Istio 的工作原理 v1.26.0
  • 计算机网络:深度解析基于链路状态的内部网关协议IS-IS
  • OpenHarmony 开源鸿蒙南向开发——linux下使用make交叉编译第三方库——gmp
  • 赛季7靶场 - Environment
  • 死锁的形成
  • 国产Excel处理控件Spire.XLS系列教程:C# 将Excel文件转换为Markdown格式
  • 线程邮箱框架与示例
  • 《Spring Boot 3.0全新特性详解与实战案例》
  • 科学选择差分探头输入阻抗的方法
  • Liunx ContOS7 安装部署 Docker
  • RabbitMQ ②-工作模式
  • Rust 智能指针全解析:从原理到实践
  • 基于DeepSeek的韦恩图绘制:方法、优化与应用
  • NX884NX891美光固态闪存NX895NX907
  • ET2120工业Lora数传终端RS485串口4*AIAO+Moubus RTU
  • 北斗导航 | RTKLib中模糊度解算详解,公式,代码
  • 【愚公系列】《Manus极简入门》028-创业规划顾问:“创业导航仪”
  • Python - 如何打包并发布 Python 库到 PyPI
  • 运维体系架构规划
  • VBA -- 学习Day3
  • Java设计模式之抽象工厂模式:从入门到精通
  • 工业设计破局密码:3D 可视化技术点燃产业升级引擎
  • 如何将邮件送达率从60%提升到95%
  • 【Bootstrap V4系列】学习入门教程之 组件-表单(Forms)高级用法
  • 生产安全管理系统标杆
  • 【python】Calculate the Angle of a Triangle