go-zero整合Excelize并实现Excel导入导出

go-zero整合Excelize并实现Excel导入导出

本教程基于go-zero微服务入门教程,项目工程结构同上一个教程。

本教程主要实现go-zero框架整合Excelize,并暴露接口实现Excel模板下载、Excel导入、Excel导出。

go-zero微服务入门教程:https://blog.csdn.net/u011019141/article/details/136233473

本文源码:https://gitee.com/songfayuan/go-zero-demo (教程源码分支:6.zero整合Excelize操作Excel)

准备工作

  • 如不熟悉go-zero项目的,请先查看上一篇go-zero微服务入门教程

安装依赖

Excelize官方文档

项目工程父级目录下执行如下指令安装依赖:

# 下载安装Excelize
go get github.com/xuri/excelize/v2

编写API Gateway代码

编写api文件

excel.api

在api目录下创建新目录doc/excel,在excel目录下创建excel.api文件。

syntax = "v1"info(title: "excel操作相关"desc: "excel操作相关"author: "宋发元"
)type (ExcelImportReq {DeptId string `json:"deptId"`            // 部门id(Content-Type: form-data)File interface{} `json:"file,optional"`  // excel文件(Content-Type: form-data)}ExcelImportData {Total int64 `json:"total"`      // 导入总数Success int64 `json:"success"`  // 导入成功数Msg string `json:"msg"`         // 提示信息}ExcelImportResp {Code int64 `json:"code"`Message string `json:"message"`Data ExcelImportData `json:"data"`}ExcelExportlReq{TimeStart string `form:"timeStart,optional"`                      // 时间(开始) yyyy-mm-ddTimeEnd   string `form:"timeEnd,optional"`                        // 时间(结束) yyyy-mm-dd}DefaultResponse {Code    int64  `json:"code,default=200"`Message string `json:"message,default=操作成功"`}
)@server(group : excel/testprefix : /excel/test
)service admin-api {@doc (summary :"excel模板下载")@handler ExcelTemplateDownloadget /excel/templateDownload@doc(summary :"excel导入")@handler ExcelImportpost /excel/excelImport (ExcelImportReq) returns (ExcelImportResp)@doc(summary :"excel导出")@handler ExcelExportget /excel/excelExport (ExcelExportlReq)returns (DefaultResponse)
}
admin.api

在api/doc/admin.api文件添加配置信息。

import "excel/excel.api"

用goctl生成API Gateway代码

生成方法同上篇文章,自行查看。但是此处要基于admin.api文件去生成代码,如果基于excel.api生成,则生成的代码只有excel.api定义的接口代码,其他api文件定义的接口代码不被生成。

api新增文件操作配置

以下操作在api模块执行。

admin-api.yaml

admin-api.yaml配置文件新增文件操作配置信息,如下:

#文件
UploadFile:MaxFileNum: 100MaxFileSize: 104857600  # 100MBSavePath: template/uploads/TemplatePath: template/excel/

config.go

config.go文件中新增UploadFile配置信息,如下:

type Config struct {rest.RestConfSysRpc zrpc.RpcClientConf//这里新增UploadFile UploadFile
}type UploadFile struct {MaxFileNum   int64MaxFileSize  int64SavePath     stringTemplatePath string
}

修改API Gateway代码

exceltemplatedownloadlogic.go

修改api/internal/logic/excel/test/exceltemplatedownloadlogic.go里的ExcelTemplateDownload方法,完整代码如下:

package testimport ("context""go-zero-demo/common/errors/errorx""net/http""os""github.com/zeromicro/go-zero/core/logx""go-zero-demo/api/internal/svc"
)type ExcelTemplateDownloadLogic struct {logx.Loggerctx    context.ContextsvcCtx *svc.ServiceContextwriter http.ResponseWriter
}func NewExcelTemplateDownloadLogic(ctx context.Context, svcCtx *svc.ServiceContext, writer http.ResponseWriter) *ExcelTemplateDownloadLogic {return &ExcelTemplateDownloadLogic{Logger: logx.WithContext(ctx),ctx:    ctx,svcCtx: svcCtx,writer: writer,}
}func (l *ExcelTemplateDownloadLogic) ExcelTemplateDownload() (err error) {SavePath := l.svcCtx.Config.UploadFile.TemplatePathfilePath := "demo_excel_template.xlsx"fullPath := SavePath + filePathfileName := "Excel导入模板.xlsx"//fullPath = "/Users/songfayuan/GolandProjects/go-zero-demo/template/excel/demo_excel_template.xlsx"  //测试地址,绝对路径_, err = os.Stat(fullPath)if err != nil || os.IsNotExist(err) {return errorx.New("文件不存在")}bytes, err := os.ReadFile(fullPath)if err != nil {return errorx.New("读取文件失败")}l.writer.Header().Add("Content-Type", "application/octet-stream")l.writer.Header().Add("Content-Disposition", "attachment; filename= "+fileName)l.writer.Write(bytes)return
}

excelimportlogic.go

修改api/internal/logic/excel/test/excelimportlogic.go里的ExcelImport方法,完整代码如下:

package testimport ("context""fmt""github.com/xuri/excelize/v2""github.com/zeromicro/go-zero/core/mapping""go-zero-demo/common/errors/errorx""go-zero-demo/common/utils""path/filepath""strings""go-zero-demo/api/internal/svc""go-zero-demo/api/internal/types""github.com/zeromicro/go-zero/core/logx"
)type ExcelImportLogic struct {logx.Loggerctx    context.ContextsvcCtx *svc.ServiceContext
}type excelDataForDept struct {DeptId       string `json:"DeptId,optional" excel:"col=1"`       // 第1列:部门idParentDeptId string `json:"ParentDeptId,optional" excel:"col=2"` // 第2列:上级部门idDeptName     string `json:"DeptName,optional" excel:"col=3"`     // 第3列:部门名称Level        string `json:"Level,optional" excel:"col=4"`        // 第4列:部门等级(分级名称)
}type excelDataForMember struct {DeptId  string `json:"DeptId,optional" excel:"col=1"`  // 第1列:部门Name    string `json:"Name,optional" excel:"col=2"`    // 第2列:姓名Account string `json:"Account,optional" excel:"col=3"` // 第3列:帐号Level   string `json:"Level,optional" excel:"col=4"`   // 第4列:等级(分级名称)IpAddr  string `json:"IpAddr,optional" excel:"col=5"`  // 第5列:IPMacAddr string `json:"MacAddr,optional" excel:"col=6"` // 第6列:MAC
}var (validUploadFileExt = map[string]any{".xlsx": nil,".xls":  nil,}
)func NewExcelImportLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ExcelImportLogic {return &ExcelImportLogic{Logger: logx.WithContext(ctx),ctx:    ctx,svcCtx: svcCtx,}
}func (l *ExcelImportLogic) ExcelImport(req *types.ExcelUploadReq) (resp *types.ExcelImportResp, err error) {if _, ok := validUploadFileExt[strings.ToLower(filepath.Ext(req.File.FileHeader.Filename))]; !ok {return nil, errorx.New("无效的文件格式")}// 打开文件f, err := excelize.OpenReader(req.File.File)if err != nil {return nil, errorx.New("无效的文件")}/* 解析部门Sheet数据 start */// 解析文件参数var excelDept []excelDataForDeptif excelDept, err = parseFileDept(f); err != nil {return}// formatfor _, i := range excelDept {fmt.Printf("Excel数据:%v/%v/%v/%v", i.DeptId, i.ParentDeptId, i.DeptName, i.Level)}/* 解析部门Sheet数据 end *//* 解析用户Sheet数据 start */// 解析文件参数var excelMember []excelDataForMemberif excelMember, err = parseFileUser(f); err != nil {return}// formatfor _, i := range excelMember {fmt.Printf("Excel数据:%v/%v/%v/%v/%v/%v", i.DeptId, i.Name, i.Account, i.Level, i.IpAddr, i.MacAddr)}/* 解析用户Sheet数据 end */return &types.ExcelImportResp{Code:    200,Message: "导入成功",Data: types.ExcelImportData{Total:   10,Success: 10,Msg:     "成功",},}, nil
}// 解析部门Sheet数据
func parseFileDept(f *excelize.File) ([]excelDataForDept, error) {// 解析参数(可选)excelOption := utils.ExcelOption{Sheet: "部门", StartRow: 2}// 映射回调all := make([]excelDataForDept, 0)cbHandler := func(data map[string]interface{}) error {temp := excelDataForDept{}err := mapping.UnmarshalJsonMap(data, &temp)if err != nil {return err}all = append(all, temp)return nil}// 映射if err := utils.ParseExcel(f, excelDataForDept{}, cbHandler, excelOption); err != nil {return nil, errorx.New("解析文件时出错:" + err.Error())}if len(all) == 0 {return nil, errorx.New("文件中无有效数据")}return all, nil
}// 解析用户Sheet数据
func parseFileUser(f *excelize.File) ([]excelDataForMember, error) {// 解析参数(可选)excelOption := utils.ExcelOption{Sheet: "用户", StartRow: 2}// 映射回调all := make([]excelDataForMember, 0)cbHandler := func(data map[string]interface{}) error {temp := excelDataForMember{}err := mapping.UnmarshalJsonMap(data, &temp)if err != nil {return err}all = append(all, temp)return nil}// 映射if err := utils.ParseExcel(f, excelDataForMember{}, cbHandler, excelOption); err != nil {return nil, errorx.New("解析文件时出错:" + err.Error())}if len(all) == 0 {return nil, errorx.New("文件中无有效数据")}return all, nil
}

excelexportlogic.go

修改api/internal/logic/excel/test/excelexportlogic.go里的ExcelExport方法,完整代码如下:

package testimport ("context""fmt""github.com/xuri/excelize/v2""net/http""go-zero-demo/api/internal/svc""go-zero-demo/api/internal/types""github.com/zeromicro/go-zero/core/logx"
)type ExcelExportLogic struct {logx.Loggerctx    context.ContextsvcCtx *svc.ServiceContextwriter http.ResponseWriter
}type cellValue struct {sheet stringcell  stringvalue string
}func NewExcelExportLogic(ctx context.Context, svcCtx *svc.ServiceContext, writer http.ResponseWriter) *ExcelExportLogic {return &ExcelExportLogic{Logger: logx.WithContext(ctx),ctx:    ctx,svcCtx: svcCtx,writer: writer,}
}func (l *ExcelExportLogic) ExcelExport(req *types.ExcelExportlReq) (resp *types.DefaultResponse, err error) {//这里仅演示Excel导出逻辑,真实数据自己增加对应的查询逻辑。excelFile := excelize.NewFile()//insert titlecellValues := make([]*cellValue, 0)cellValues = append(cellValues, &cellValue{sheet: "sheet1",cell:  "A1",value: "序号",}, &cellValue{sheet: "sheet1",cell:  "B1",value: "IP地址",}, &cellValue{sheet: "sheet1",cell:  "C1",value: "账号",}, &cellValue{sheet: "sheet1",cell:  "D1",value: "姓名",}, &cellValue{sheet: "sheet1",cell:  "E1",value: "最近访问时间",}, &cellValue{sheet: "sheet1",cell:  "F1",value: "设备状态",}, &cellValue{sheet: "sheet1",cell:  "G1",value: "访问分级",})// 创建一个工作表index, _ := excelFile.NewSheet("Sheet1")// 设置工作簿的默认工作表excelFile.SetActiveSheet(index)//插入表格头for _, cellValue := range cellValues {excelFile.SetCellValue(cellValue.sheet, cellValue.cell, cellValue.value)}//设置表格头字体样式styleId, err := excelFile.NewStyle(&excelize.Style{Font: &excelize.Font{Bold:   true,  //黑体Italic: false, //倾斜Family: "宋体",Size:   14,//Color:  "微软雅黑",},})if err != nil {fmt.Println(err)}for _, data := range cellValues {excelFile.SetCellStyle(data.sheet, data.cell, data.cell, styleId)}excelFile.SetColWidth("sheet1", "B", "G", 20)cnt := 1for i := 0; i <= 6; i++ {cnt = cnt + 1for k1, v1 := range cellValues {switch k1 {case 0:v1.cell = fmt.Sprintf("A%d", cnt)v1.value = fmt.Sprintf("%d", i+1)case 1:v1.cell = fmt.Sprintf("B%d", cnt)v1.value = "1"case 2:v1.cell = fmt.Sprintf("C%d", cnt)v1.value = "2"case 3:v1.cell = fmt.Sprintf("D%d", cnt)v1.value = "3"case 4:v1.cell = fmt.Sprintf("E%d", cnt)v1.value = "4"case 5:v1.cell = fmt.Sprintf("F%d", cnt)v1.value = "5"case 6:v1.cell = fmt.Sprintf("G%d", cnt)v1.value = "6"}}for _, vc := range cellValues {excelFile.SetCellValue(vc.sheet, vc.cell, vc.value)}}fileName := "ABCD.xlsx"//如果是下载,则需要在Header中设置这两个参数l.writer.Header().Add("Content-Type", "application/octet-stream")l.writer.Header().Add("Content-Disposition", "attachment; filename= "+fileName)//l.writer.Header().Add("Content-Transfer-Encoding", "binary")excelFile.Write(l.writer)return
}

exceltemplatedownloadhandler.go

exceltemplatedownloadhandler.go代码微调整。

func ExcelTemplateDownloadHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {l := test.NewExcelTemplateDownloadLogic(r.Context(), svcCtx, w)err := l.ExcelTemplateDownload()if err != nil {httpx.ErrorCtx(r.Context(), w, err)} else {httpx.Ok(w)}}
}

excelimporthandler.go

excelimporthandler.go代码微调整。

func ExcelImportHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {var req types.ExcelUploadReqreq.DeptId = r.FormValue("deptId")f, fh, e := utils.ParseFile(r, "file")if e != nil {httpx.Error(w, e)return}req.File = &types.File{File: f, FileHeader: fh}l := test.NewExcelImportLogic(r.Context(), svcCtx)resp, err := l.ExcelImport(&req)if err != nil {httpx.ErrorCtx(r.Context(), w, err)} else {httpx.OkJson(w, resp)}}
}

excelexporthandler.go

excelexporthandler.go代码微调整。

func ExcelExportHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {var req types.ExcelExportlReqif err := httpx.Parse(r, &req); err != nil {httpx.ErrorCtx(r.Context(), w, err)return}l := test.NewExcelExportLogic(r.Context(), svcCtx, w)resp, err := l.ExcelExport(&req)if err != nil {httpx.ErrorCtx(r.Context(), w, err)} else {httpx.OkJsonCtx(r.Context(), w, resp)}}
}

base.go

在路径api/internal/types下创建base.go,内容如下:

package typesimport "mime/multipart"type File struct {File       multipart.FileFileHeader *multipart.FileHeader
}type ExcelUploadReq struct {DeptId string `json:"deptId"` // 部门idFile   *File  `json:"file"`   // excel文件
}

Excel导入模板

在项目根目录下创建template/excel目录,里面存放Excel导入模板demo_excel_template.xlsx。

模板内容见源码!!!
在这里插入图片描述
在这里插入图片描述

完整调用演示

最后,在根目录go-zero-demo执行下命令。

go mod tidy

运行rpc服务

运行方法同上篇文章,具体查看教程go-zero微服务入门教程完整调用演示部分。

运行api

运行方法同上篇文章,具体查看教程go-zero微服务入门教程完整调用演示部分。

api调用

以下调用采用postman调用。

Excel模板下载
localhost:8888/excel/test/excel/templateDownload
Excel导入
localhost:8888/excel/test/excel/excelImport
Excel导出
localhost:8888/excel/test/excel/excelExport

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/27297.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mysql学习(九)——存储引擎

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 七、存储引擎7.1 MySQL体系结构7.2 存储引擎简介7.3 存储引擎特点7.4 存储引擎选择7.5 总结 七、存储引擎 7.1 MySQL体系结构 连接层&#xff1a;最上层是一些客户…

自然语言处理领域的重大挑战:解码器 Transformer 的局限性

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

聚焦赛宁网安竞赛平台+赛事服务,引领网络安全竞赛新潮流

第八届XCTF总决赛将在2024年6月22日于中国成都震撼开启&#xff0c;本届总决赛分为个人Live Solo和团队KOH巅峰对决两个赛道&#xff0c;从个人和团队多角度全方位考察参赛人员的竞技水平。 巅峰对决 智慧的火花在此碰撞 个人Live Solo赛制 Live Solo赛分为晋级赛和Solo赛。…

贝壳APP渗透测试WP

前期配置 环境说明 使用PIXEL 4手机&#xff0c;为Android 12系统 APP名为贝壳找房&#xff0c;包名com.lianjia.beike&#xff0c;版本号3.01.10&#xff0c;截至2024/05/07为最新版&#xff0c;小米应用市场下载 绕过反Frida机制 可以参考往期推送&#xff0c;《绕过最新…

2分钟用手机开发一个ChatBot

前言&#xff1a; 在上一期&#xff0c;我们测评了CodeFlying&#xff0c;用它开发出了一个复杂推文管理系统&#xff0c;然后体验了一下它的热门应用&#xff1a;AI智能机器人。今天咱就继续用CodeFlying来开发一个属于我们自己的聊天机器人。 老规矩&#xff0c;我们先在手机…

【qt】平面CAD(计算机辅助设计 )项目 上

CAD 一.前言二.界面设计三.提升类四.接受槽函数五.实现图形action1.矩形2.椭圆3.圆形4.三角形5.梯形6.直线7.文本 六.总结 一.前言 用我们上节课刚刚学过的GraphicsView架构来绘制一个可以交互的CAD项目! 效果图: 二.界面设计 添加2个工具栏 需要蔬菜的dd我! 添加action: …

遗传算法求解车间调度问题(附python代码)

背景介绍 车间调度问题&#xff08;Job Shop Scheduling Problem, JSSP&#xff09;是一类经典的组合优化问题&#xff0c;它在制造业和生产管理中有着广泛的应用。JSSP 的目标是对车间中的一系列作业进行排程&#xff0c;以使得作业在不同机器上的加工顺序是最优的&#xff0…

万相台的功能是什么?如何使用万相台?

1.特点&#xff1a; 万相台是一个智能渠道&#xff0c;可控性弱&#xff0c;高转化&#xff0c;人群&关键词是黑盒&#xff1b; 2.场景多&#xff1a; 有拉新快、活动加速、上新快、货品加速、活动加速、多目标直投、全站推等&#xff1b; 3.扣费逻辑&#xff1a;cpc付…

Sm4【国密4加密解密】

当我们开发金融、国企、政府信息系统时&#xff0c;不仅要符合网络安全的等保二级、等保三级&#xff0c;还要求符合国密的安全要求&#xff0c;等保测评已经实行很久了&#xff0c;而国密测评近两年才刚开始。那什么是密码/国密&#xff1f;什么是密评&#xff1f;本文就关于密…

Linux:线程概念 线程控制

Linux&#xff1a;线程概念 & 线程控制 线程概念轻量级进程 LWP页表 线程控制POSIX 线程库 - ptherad线程创建pthread_createpthread_self 线程退出pthread_exitpthread_cancelpthread_joinpthread_detach 线程架构线程与地址空间线程与pthread动态库 线程的优缺点 线程概念…

机器学习与数据挖掘知识点总结(二)分类算法

目录 1、什么是数据挖掘 2、为什么要有数据挖掘 3、数据挖掘用在分类任务中的算法 朴素贝叶斯算法 svm支持向量机算法 PCA主成分分析算法 k-means算法 决策树 1、什么是数据挖掘 数据挖掘是从大量数据中发现隐藏在其中的模式、关系和规律的过程。它利用统计学、机器学…

14.shell awk数组

awk数组 awk数组awk数组示例Nginx日志分析 awk数组 1.什么是awk数组 数组其实也算是变量,传统的变量只能存储一个值,但数组可以存储多个值 2.awk数组应用场景 通常用来统计、比如:统计网站访问TOP10、网站url访问TOP10等等 3.awk数组统计技巧 1.在awk中,使用数组时,不仅可以…

Interview preparation--RabbitMQ

AMQP AMQP(Advanced Message Queueing protocol). 高级消息队列协议&#xff0c;是进程之间床底一步新消息的网络协议AMQP工作原理如下&#xff1a; 发布者&#xff08;Publisher&#xff09;发布消息&#xff08;Message&#xff09;经过交换机&#xff08;Exchange&#xff…

新视窗新一代物业管理系统 GetCertificateInfoByStudentId SQL注入漏洞复现

0x01 产品简介 新视窗物业管理系统属于专家型的物业管理软件,能够给物业公司内部管理提供全面的解决方案,具有房产管理、客户管理、租赁管理、仪表管理、财务收费、发票管理、合同管理、仓储管理、设施设备管理、客户服务管理、会员管理、人事管理、资产管理、日常办公、档案…

HTML+CSS 动态卡片

效果演示 实现了一个带有动态背景和图片放大效果的卡片展示。卡片的背景是由两种颜色交替组成的斜线条纹&#xff0c;同时背景会以一定速度循环滚动。当鼠标悬停在卡片上时&#xff0c;卡片的图片会放大&#xff0c;并且卡片的背景会变为彩色。 Code HTML <!DOCTYPE html&…

简单聊一下Oracle,MySQL,postgresql三种锁表的机制,行锁和表锁

MySQL&#xff1a; MySQL使用行级锁定和表级锁定。行级锁定允许多个会话同时写入表&#xff0c;适用于多用户、高并发和OLTP应用。表级锁定只允许一个会话一次更新表&#xff0c;适用于只读、主要读取或单用户应用。 比如mysql开启一个窗口执行 begin; update xc_county_a…

【STM32】飞控设计

【一些入门知识】 1.飞行原理 【垂直运动】 当 mg&#xff1e;F1F2F3F4&#xff0c;此时做下降加速飞行 当 mg&#xff1c;F1F2F3F4&#xff0c;此时做升高加速飞行 当 mgF1F2F3F4 &#xff0c;此时垂直上保持匀速飞行。 【偏航飞行】 ω 4 ω 2 ≠ ω 1 ω 3 就会产生水…

我的考研经历

当我写下这篇文章时&#xff0c;我已经从考研 的失败中走出来了&#xff0c;考研的整个过程都写在博客日志里面了&#xff0c;在整理并阅读考研的日志时&#xff0c;想写下一篇总结&#xff0c;也算是为了更好的吸取教训。 前期日志模板&#xff1a;时间安排的还算紧凑&#x…

【启明智显分享】Model系列工业级HMI芯片:开源RISC-V+RTOS实时系统,开放!高效!

前言 「Model系列」芯片是启明智显针对工业、行业以及车载产品市场推出的系列HMI芯片&#xff0c;主要应用于工业自动化、智能终端HMI、车载仪表盘、两轮车彩屏仪表、串口屏、智能中控、智能家居、充电桩显示屏、储能显示屏、工业触摸屏等领域。此系列具有高性能、低成本的特点…

C4D如何预览动画?C4D动画云渲染助力

C4D是一款功能丰富的3D设计软件&#xff0c;以其快速的预览渲染和多样的渲染插件而闻名&#xff0c;其卓越的渲染效果赢得了CG行业专业人士的广泛赞誉。尽管C4D的渲染功能十分强大&#xff0c;但对于初学者而言&#xff0c;其复杂的渲染设置可能会带来一些挑战。本文一起来看看…