go从服务器导出Excel文件流给前端浏览器及前端如何解析

go服务端导出.xlsx格式的Excel文件流给前端浏览器

上传文件(以".xlsx"类型的Excel为例)并读取数据请参考https://blog.csdn.net/fbbqt/article/details/106016895

import (
	"github.com/tealeg/xlsx"
	bm "github.com/bilibili/kratos/pkg/net/http/blademaster"
)
//生成操作日志Excel文件并返回给前端,具体逻辑实现
func GetOperationLogExcel(ctx *bm.Context, data []database.OperationLog) error {
	//设置导出的字体类型和大小
	xlsx.SetDefaultFont(18,"楷体")
	style := &xlsx.Style{}
	style.Fill = *xlsx.NewFill("solid", "EFEFDE", "EFEFDE")
	style.Border = xlsx.Border{RightColor: "FF"}
	file := xlsx.NewFile()
	sheet, err := file.AddSheet("Sheet1")
	if err != nil {
		return err
	}
	//设置表格头
	row := sheet.AddRow()
	//row.SetHeightCM(1)            //设置每行的高度
	for _, title := range Title { //looping from 0 to the length of the array
		cell := row.AddCell()
		cell.Value = title
		//设置字体为红色
		cell.GetStyle().Font.Color = "00FF0000"
	}
	//写入数据
	for _, content := range data {
		row = sheet.AddRow()
		//row.SetHeightCM(1)                   //设置每行的高度
		cell := row.AddCell()                //cell 1
		cell.Value = content.Username        //账户名称
		cell = row.AddCell()                 //cell 2
		cell.Value = content.Realname        //姓名
		cell = row.AddCell()                 //cell 3
		cell.Value = content.Role            //账户角色
		cell = row.AddCell()                 //cell 4
		cell.Value = content.OperationModule //操作模块
		cell = row.AddCell()                 //cell 5
		cell.Value = content.OperationDetail //详细操作
		cell = row.AddCell()                 //cell 6
		//content.OperationTime前面加',是因为在linux系统本地生成时默认带。
		//但传递给windows前端生成excel的时候,会去掉',会导致格式变化,会变成跟Windows系统上的时间格式一样。
		cell.Value = "'" + content.OperationTime //操作时间
		cell = row.AddCell()                     //cell 7
		cell.Value = content.OperationResult     //操作结果
	}

	// 设置头信息:Content-Disposition ,消息头指示回复的内容该以何种形式展示,
	// 是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地
	// Content-Disposition: inline
	// Content-Disposition: attachment
	// Content-Disposition: attachment; filename="filename.后缀"
	// 第一个参数或者是inline(默认值,表示回复中的消息体会以页面的一部分或者
	// 整个页面的形式展示),或者是attachment(意味着消息体应该被下载到本地;
	// 大多数浏览器会呈现一个“保存为”的对话框,将filename的值预填为下载后的文件名,
	// 假如它存在的话)
	ctx.Writer.Header().Add("Content-Disposition", `attachment; filename="OperationLog.xlsx"`)
	//xls,设置后缀为xls类型的表格
	//w.Header().Add("Content-Type", "application/vnd.ms-excel")
	//xlsx,设置后缀为xlsx类型的表格
	ctx.Writer.Header().Add("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

	//eTime := time.Now().Format("2006-01-02 15:04:05")
	//path := fmt.Sprint("/home/bqpbqt/nginx-file/", "_", eTime, ".xlsx") //生成excel文件到该服务目录下
	//err = file.Save(path)
	//if err != nil {
	//	return err
	//}
	先把文件保存在服务器上,然后通过 http.ServeFile返回给浏览器
	//http.ServeFile(ctx.Writer,ctx.Request,path)

	//通过静态路由访问静态目录(./data)下的文件
	//path := fmt.Sprint("./data/financial/", "_", eTime, ".xlsx") //生成excel文件到该服务目录下
	//return fmt.Sprint("/financial/", "_", eTime, ".xlsx")

	//http.ServeContent采取的是直接把数据写入http.responsewriter (即http包中的responsewriter)中进行返回
	var buffer bytes.Buffer
	if e := file.Write(&buffer); e != nil {
		return e
	}
	r := bytes.NewReader(buffer.Bytes())
	///
	//这块是调用GetFileType()函数,就可以拿到文件流的文件后缀
	fSrc, _ := ioutil.ReadAll(r)
	log.Info("ggggggggggg",GetFileType(fSrc[:10]))
	//
	//最主要的一句,返回给浏览器
	http.ServeContent(ctx.Writer, ctx.Request, "", time.Now(), r)

	return nil
}
///
//TODO 以下是判断文件流的文件后缀是不是 .xlsx
// 获取前面结果字节的二进制
func bytesToHexString(src []byte) string {
	res := bytes.Buffer{}
	if src == nil || len(src) <= 0 {
		return ""
	}
	temp := make([]byte, 0)
	for _, v := range src {
		sub := v & 0xFF  //其本质原因就是想保持二进制补码的一致性
		hv := hex.EncodeToString(append(temp, sub))
		if len(hv) < 2 {
			res.WriteString(strconv.FormatInt(int64(0), 10))
		}
		res.WriteString(hv)
	}
	return res.String()
}

// 用文件前面几个字节来判断
// fSrc: 文件字节流(就用前面几个字节)
func GetFileType(fSrc []byte) string {
	var fileType string
	fileCode := bytesToHexString(fSrc)
	log.Info("....", fileCode)
	if strings.HasSuffix(fileCode, ".xlsx") {
		fileType = ".xlsx"
		log.Info("nnnnnnnnnnnnnnnnnn", fileType)
	}
	log.Info("mmmmmmmmmmmmmmmmmm", fileType)
	return fileType
}

前端接收文件流

  1. 不管用什么方法,一定要记得设置responseType!!!,否则导出的文件报错,打不开
//xlsx类型的Excel文件要设置成arraybuffer类型,如下所示:
axios.post(url_post,params_post,{responseType: 'arraybuffer'}).then((res) => {}
  1. 若后端在往前端返回的时候,没有忽略失败时的错误,则需要考虑以下的操作:

设置返回的responseType类型是arraybuffer后,如果导出失败,本应返回的json格式数据也会变成arraybuffer,这种情况下就需要对这个流进行处理,判断它是导出失败的json,还是导出成功的流文件,所以在上面的导出代码之前添加转json并进行判断:

const tempBlob = new Blob( [res.data], {type: ‘application/json} )
// 通过 FileReader 读取这个 blob
const reader = new FileReader()
reader.onload = e => {
  const res = e.target.result
  // 此处对fileReader读出的结果进行JSON解析
  // 可能会出现错误,需要进行捕获
  try {
    const json = JSON.parse(res)
    if (json) {
      // 解析成功说明后端导出出错,进行导出失败的操作,并直接返回
      return
    }
  } catch (err) {
    // 该异常为无法将字符串转为json
    // 说明返回的数据是一个流文件
    // 不需要处理该异常,只需要捕获即刻
  }
  // 如果代码能够执行到这里,说明后端给的是一个流文件,再执行上面导出的代码
  // do export code
}
// 将blob对象以文本的方式读出,读出完成后将会执行 onload 方法
reader.readAsText(tempBlob)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值