go实现导出excel或csv文件
很早之前的一个项目需求要,需要把公司信息列表和漏洞信息列表导出excel文件,没有很高要求,能看就行吧,就做了导出csv的两个接口。 最近看我的CSDN发现好久没有写了,惭愧啊!其实遇到的问题,我都有详细记录,自己能看懂,但分享给别人也能看懂就需要好好写了,尽量有空整吧,需要坚持!
实现代码
我把实现代码简单写了一下,是用调用接口的方式实现的,用的gin 包。
最近又有了导出CSV文件的需求,就重新修改了一下这篇文章,根据不同情况分类4类,用4个api来分别请求实现。
第一个接口是这个文章之前的代码,直接返回了文件,请求接口就下载文件,因为在代码里创建了文件。
第二、三、四个接口都是,代码里只返回对应文件格式的数据,需要前端进行处理保存到对应的文件中实现下载。
先说一下优缺点,第一种,需要后端在进程里创建对应的文件保存,最后还要删除掉,如果请求多或文件内容多,都会给后端内存造成很大压力,所以建议使用后面的api,让前端直接获取数据进行保存文件。也要看各自的需求吧,看代码。
Content-Type
下载文件一般要设置对应的文件格式,就是Content-Type ,包括 http 的mime type,知道这个内容的可以跳过直接先看代码,有疑问可以先看下这几篇文章:
1, HTTP Content-Type(MIME List)介绍
2, 阿里云 MIME List 介绍
3, 菜鸟学院 MEMI List 介绍
package main
import ("bytes""encoding/csv""fmt""net/http""os""time""github.com/gin-gonic/gin""github.com/tealeg/xlsx"
)type Server struct {engine *gin.Engine
}func NewServer() *Server {ser := &Server{// 用的gin.Default()引擎,自带Logger and Recovery两个中间件,也可以用gin.New(),不带中间件engine: gin.Default(), }return ser
}// 这个是为了造数据,可以忽略。
type Student struct {ID stringName stringAge stringScore stringAddr stringDate string
}
// 造数据的方法
func (s *Server) getData() []*Student {res := make([]*Student, 0, 25)for i := 1; i <= 20; i++ {res = append(res, &Student{ID: fmt.Sprintf("2021000000%d", i),Name: fmt.Sprintf("辣条精-%d", i),Age: fmt.Sprintf("1%d", i),Score: fmt.Sprintf("8%d", i),Addr: fmt.Sprintf("辣条小镇太平村-%d号", i),Date: fmt.Sprintf("2012-12-%d", i),})}return res
}// 这是对应的4个接口,下面会具体说明一下不同接口的作用。
func (s *Server) Start() {gin.SetMode(gin.ReleaseMode)s.engine.GET("/csv", s.csvApi) //1,请求后直接下载csv文件s.engine.GET("/csv_data", s.saveCSVApi) //2,请求后,返回csv格式的文件数据byte,需要前端保存到一个对应格式的文件中s.engine.GET("/xlsx_data", s.saveXlxsApi) //3,请求后,返回xlsx格式的文件数据byte,需要前端保存到一个对应格式的文件中s.engine.GET("/xlsx_csv", s.saveXlsxCSVApi) //4,请求后,使用xlsx返回csv格式的文件数据byte,需要前端保存到一个对应格式的文件中s.engine.Run(":9999")
}//启动进程
func main() {NewServer().Start()
}// 接口一
// 请求接口后,会直接下载csv格式文件,使用 "encoding/csv" 包实现,代码里直接创建了文件,最后还删除,不然也会给服务器压力,或定期删除。
func (s *Server) csvApi(c *gin.Context) {filename, err := s.toCsv()if err != nil {fmt.Println("t.toCsv() failed == ", err)}if filename == "" {fmt.Println("export excel file failed == ", filename)}defer func() {err := os.Remove("./" + filename) //下载后,删除文件if err != nil {fmt.Println("remove excel file failed", err)}}()c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))c.Writer.Header().Add("Content-Type", "application/octet-stream") //设置下载文件格式,流式下载c.File("./" + filename) //直接返回文件
}// 接口一 function
func (t *Server) toCsv() (string, error) {//获取数据data := t.getData()strTime := time.Now().Format("20060102150405")//创建csv文件filename := fmt.Sprintf("学生信息-%s.csv", strTime)xlsFile, fErr := os.OpenFile("./"+filename, os.O_RDWR|os.O_CREATE, 0766)if fErr != nil {fmt.Println("Export:created excel file failed ==", fErr)return "", fErr}defer xlsFile.Close()//开始写入内容//写入UTF-8 BOM,此处如果不写入就会导致写入的汉字乱码xlsFile.WriteString("\xEF\xBB\xBF")wStr := csv.NewWriter(xlsFile)wStr.Write([]string{"学生学号", "学生姓名", "学生年龄", "学生成绩", "家庭地址", "学生生日"})for _, s := range data {wStr.Write([]string{s.ID, s.Name, s.Age, s.Score, s.Addr, s.Date})}wStr.Flush() //写入文件return filename, nil
}// 接口二
// 返回csv文件格式的 []byte数据,使用 "encoding/csv" 包实现,用于导出csv文件。
func (s *Server) saveCSVApi(c *gin.Context) {respData, err := s.SetValueToCSV()if err != nil {fmt.Println("SetValueToCSV failed == ", err.Error())}//指定下载文件名,可以注释掉,让前端处理文件名c.Header("Content-Disposition", "attachment; filename=StudentList.csv") c.Header("Content-Type", "text/csv") //设置为 .csv 格式文件c.Data(http.StatusOK, "text/csv", respData)
}// 接口二 function
func (t *Server) SetValueToCSV() ([]byte, error) {data := t.getData() //获取数据 //内容先写入buffer缓存buff := new(bytes.Buffer)//写入UTF-8 BOM,此处如果不写入就会导致写入的汉字乱码buff.WriteString("\xEF\xBB\xBF")wStr := csv.NewWriter(buff)wStr.Write([]string{"学生学号", "学生姓名", "学生年龄", "学生成绩", "家庭地址", "学生生日"})for _, s := range data {wStr.Write([]string{s.ID, s.Name, s.Age, s.Score, s.Addr, s.Date})}wStr.Flush() // 返回[]byte数据return buff.Bytes(), nil
}//接口三
//返回xlsx文件格式的 []byte数据,使用 "github.com/tealeg/xlsx" 包实现,用于导出xlsx文件。
func (s *Server) saveXlxsApi(c *gin.Context) {respData, err := s.SetValueToXlsx()if err != nil {fmt.Println("SetValueToCSV failed == ", err.Error())}//指定下载文件名,可以注释掉,让前端处理文件名c.Header("Content-Disposition", "attachment; filename=StudentList.xlsx") // 设置为 .xlsx格式文件c.Header("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")c.Data(http.StatusOK, "", respData)
}
//接口三 function
func (t *Server) SetValueToXlsx() ([]byte, error) {data := t.getData() //获取数据var file *xlsx.Filevar sheet *xlsx.Sheetvar row *xlsx.Rowvar cell *xlsx.Cellvar err errorfile = xlsx.NewFile()sheet, err = file.AddSheet("Sheet1")if err != nil {fmt.Println("init xlsx file failed, err == ", err.Error())return nil, err}row = sheet.AddRow()row.SetHeightCM(1)cell = row.AddCell()cell.Value = "学生学号"cell = row.AddCell()cell.Value = "学生姓名"cell = row.AddCell()cell.Value = "学生年龄"cell = row.AddCell()cell.Value = "学生成绩"cell = row.AddCell()cell.Value = "家庭地址"cell = row.AddCell()cell.Value = "学生生日"for _, v := range data {row1 := sheet.AddRow()cell = row1.AddCell()cell.Value = v.IDcell = row1.AddCell()cell.Value = v.Namecell = row1.AddCell()cell.Value = v.Agecell = row1.AddCell()cell.Value = v.Scorecell = row1.AddCell()cell.Value = v.Addrcell = row1.AddCell()cell.Value = v.Date}buff := new(bytes.Buffer)file.Write(buff)return buff.Bytes(), nil
}//接口四
//返回csv文件格式的 []byte数据,使用 "encoding/csv"包实现,用于导出csv文件。
func (s *Server) saveXlsxCSVApi(c *gin.Context) {// 还是使用的接口三function 包的方法,respData, err := s.SetValueToXlsx()if err != nil {fmt.Println("SetValueToXlsx failed == ", err.Error())}//指定下载文件名,可以注释掉,让前端处理文件名c.Header("Content-Disposition", "attachment; filename=StudentList.csv") c.Header("Content-Type", "text/csv") //设置为 .csv 格式文件c.Data(http.StatusOK, "text/csv", respData)
}
请求测试
接口一
请求地址: http://127.0.0.1:9999/csv,直接在浏览器里请求这个地址即可。
请求后会直接下载导出CSV文件,但是看起来不太好看,有些数据自动被转化了,点击查看的话是没有问题。
接口二
请求地址:http://127.0.0.1:9999/csv_data ,不能在浏览器直接请求,在Postman测试接口,
点击Send and Download,会显示保存 .csv 文件,可以选择保存文件目录地址和修改文件名,文件内容和接口一 一样。
接口三
请求地址:http://127.0.0.1:9999/xlsx_data ,不能在浏览器直接请求,在Postman测试接口,
点击Send and Download,会显示保存 .xlsx 文件,可以选择保存文件目录地址和修改文件名。
对比之后,可以看到,导出的excel文件,xlsx格式文件比csv格式文件 会更好看一些,可以考虑导出 .xlsx文件, 如果有要求 导出 .csv 格式文件,可以使用接口四,使用xlsx包 实现文件内容,导出.csv 文件,也是可以的,我目前使用的是 接口四。