很早之前的一个项目需求要,需要把公司信息列表和漏洞信息列表导出excel文件,没有很高要求,能看就行吧,就做了导出csv的两个接口。 最近看我的CSDN发现好久没有写了,惭愧啊!其实遇到的问题,我都有详细记录,自己能看懂,但分享给别人也能看懂就需要好好写了,尽量有空整吧,需要坚持!
我把实现代码简单写了一下,是用调用接口的方式实现的,用的gin 包。
最近又有了导出CSV文件的需求,就重新修改了一下这篇文章,根据不同情况分类4类,用4个api来分别请求实现。
第一个接口是这个文章之前的代码,直接返回了文件,请求接口就下载文件,因为在代码里创建了文件。
第二、三、四个接口都是,代码里只返回对应文件格式的数据,需要前端进行处理保存到对应的文件中实现下载。
先说一下优缺点,第一种,需要后端在进程里创建对应的文件保存,最后还要删除掉,如果请求多或文件内容多,都会给后端内存造成很大压力,所以建议使用后面的api,让前端直接获取数据进行保存文件。也要看各自的需求吧,看代码。
下载文件一般要设置对应的文件格式,就是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 string
Name string
Age string
Score string
Addr string
Date 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.File
var sheet *xlsx.Sheet
var row *xlsx.Row
var cell *xlsx.Cell
var err error
file = 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.ID
cell = row1.AddCell()
cell.Value = v.Name
cell = row1.AddCell()
cell.Value = v.Age
cell = row1.AddCell()
cell.Value = v.Score
cell = row1.AddCell()
cell.Value = v.Addr
cell = 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 文件,也是可以的,我目前使用的是 接口四。