最近在公司了个需求,主要涉及到文件导出,需要根据特定表格文件生成excel文件导出,同时对导出的excel临时保存本地,生成压缩包,将压缩包上传至OSS(Object Storage Service)后删除本地临时文件。下面的文章中将对以上涉及到的几个点编写代码实现。
Go语言对excel文件的操作
Go中对excel文件的操作主要依赖于Excelize库,提供了一组函数,可以对XLAM / XLSM / XLSX / XLTM / XLTX文件进行读写。支持读写由Microsoft Excel™2007及以后版本生成的电子表格文档。通过高兼容性支持复杂组件,并提供流API从具有大量数据的工作表中生成或读取数据。该库需要Go 1.16或更高版本。
首先通过以下命令获取excelize依赖:
ge get github.com/xuri/excelize/v2
常见的对excel文件的操作包括:生成excel表格、生成sheet页、向对应sheet页中的cell单元格内填写数据(对excel文件操作的基本步骤)。代码如下:
func genExcelFile() (err error) {
// 创建一个新的Excel文件1
f1 := excelize.NewFile()
// 在Excel中创建一个新的sheet页
f1.NewSheet("Sheet1")
// 向工作表中写入数据
if err = f1.SetCellValue("Sheet1", "A1", "Hello, World!"); err != nil {
return
}
// 创建一个新的Excel文件2
f2 := excelize.NewFile()
// 在Excel中创建一个新的sheet页
f2.NewSheet("Sheet1")
if err = f2.SetCellValue("Sheet1", "B1", 123); err != nil {
return
}
return
}
上述代码给出了对excel文件操作的基本思路实现,在公司中不可能让自己单独实现一个excel的SDK对文件进行导出操作。具体学习参考下述链接:一文搞懂Go读写Excel文件_go excel-CSDN博客
将生成的excel文件保存到本地
我在公司中做的需求需要同时导出多个excel文件,一个excel文件又包含多个sheet页,最终测试环境导出数据量达6W条之多。我需要将导出的多个excel文件生成一个压缩包。可能会问为什么需要将excel文件暂时保存到磁盘中,而不是暂时保存到内存中等所有的excel文件生成后直接在内容中将其生成zip文件。最终数据量非常大,若将其直接保存到内存中等所有excel表格生成后,将其合并生成zip文件是非常不明智的决定,对于内存的压力巨大。正确的做法每生成一个excel文件将其保存到磁盘目录中,等到所有excel文件生成后,遍历并读取所有excel文件,每遍历一个压缩一个,最终生成一个zip文件。并在将zip文件上传至OSS后删除磁盘文件。
在上面的步骤中我们得到了 file *excelize.File 类型的excel文件,在填充了对应sheet单元格中具体内容后,需要将其保存到磁盘。借助于和File文件绑定的SaveAs()方法可以实现。
在得知以上方法之后,自己编写了一个简易的文件保存方法:
const(
celFilePath = "excelFiles"
)
func saveExcelFile(file *excelize.File, sub string) (path string, err error) {
path = filepath.Join(excelFilePath, fmt.Sprintf("%s%s", sub, "test.xlsx"))
if err = file.SaveAs(path); err != nil {
return "", err
}
return path, nil
}
将对应的excel文件保存到当前项目下并且创建一个excelFiles文件夹用以保存所有excel文件。上述代码最终报错:
open excelFiles/1test.xlsx: no such file or directory
显示对应路径不存在。之前并没有创建临时目录,我本以为SavaAs方法在对应目录不存在时会创建对应的目录,和文件,但实验过后发现并非如此。在写入本地对应目录前,需要借助os.MkdirAll()函数判断对应目录是否存在,如果不存在会创建对应的目录,包含子目录。
修改后代码如下所示:
func saveExcelFile(file *excelize.File, sub string) (path string, err error) {
path = filepath.Join(excelFilePath, fmt.Sprintf("%s%s", sub, "test.xlsx"))
if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
return "", err
}
if err = file.SaveAs(path); err != nil {
return "", err
}
return path, nil
}
执行创建excel,并将excel写入本地代码后在项目文件夹下创建 excelFiles 文件夹用以存储所有excel文件,效果如下:
使用压缩工具将多个excel文件压缩为压缩包
最终根据保存的excel文件路径去遍历读取每个excel文件并将其添加到zip压缩文件中。此时需要记住 "archive/zip" 依赖中的 zip.NewWriter(zipFile) 创建zip文件写入器:
// zip压缩文件存放路径
zipFilePath = filepath.Join(excelFilePath, excelZipName)
// 创建ZIP文件
zipFile, err := os.Create(zipFilePath)
if err != nil {
return "", err
}
defer zipFile.Close()
// 创建ZIP写入器
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
随后遍历读取所有excel文件,并将其写入到zip压缩文件中。
删除临时文件及目录
使用 os.RemoveAll(excelFilePath) 可以删除对应目录下的文件及目录。
此外自己代码编写过程中学习了如何删除对应目录下的所有文件,不删除对应目录:
func deleteFiles(path string) error {
// 要删除文件的目录
// 读取目录中的所有文件
files, err := os.ReadDir(path)
if err != nil {
fmt.Println(err)
return err
}
// 遍历所有文件
for _, file := range files {
err := os.Remove(filepath.Join(path, file.Name()))
if err != nil {
fmt.Println(err)
return err
}
}
return nil
}
代码及效果展示
最终代码展示了如何创建多个excel、将excel写入本地磁盘、遍历读取磁盘excel生成zip文件、将zip文件上传到OSS(伪代码)、删除临时文件:注释defer中的删除临时目录及文件后代码如下:
package main
import (
"archive/zip"
"fmt"
"github.com/xuri/excelize/v2"
"io"
"os"
"path/filepath"
)
const (
excelFilePath = "excelFiles"
excelZipName = "wwtTest.zip"
)
func main() {
defer func() {
//os.RemoveAll(excelFilePath)
}()
genExcelFile()
}
func genExcelFile() (err error) {
f1 := excelize.NewFile() // 创建一个新的Excel文件
// 在Excel中创建一个新的工作表
f1.NewSheet("Sheet1")
// 向工作表中写入数据
if err = f1.SetCellValue("Sheet1", "A1", "Hello, World!"); err != nil {
fmt.Println(err)
return
}
f2 := excelize.NewFile() // 创建一个新的Excel文件
// 在Excel中创建一个新的工作表
f2.NewSheet("Sheet1")
if err = f2.SetCellValue("Sheet1", "B1", 123); err != nil {
fmt.Println(err)
return
}
path1, err := saveExcelFile(f1, "1")
if err != nil {
fmt.Println(err)
return
}
path2, err := saveExcelFile(f2, "2")
if err != nil {
fmt.Println(err)
return
}
// 获取zip文件
zipFilePath, _ := genZipFiles([]string{path1, path2})
fmt.Println(zipFilePath)
// 将zip文件上传至OSS
// TODO
return
}
func saveExcelFile(file *excelize.File, sub string) (path string, err error) {
path = filepath.Join(excelFilePath, fmt.Sprintf("%s%s", sub, "test.xlsx"))
if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
return "", err
}
if err = file.SaveAs(path); err != nil {
return "", err
}
return path, nil
}
func genZipFiles(filePaths []string) (zipFilePath string, err error) {
zipFilePath = filepath.Join(excelFilePath, excelZipName)
// 创建ZIP文件
zipFile, err := os.Create(zipFilePath)
if err != nil {
return "", err
}
defer zipFile.Close()
// 创建ZIP写入器
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
// 遍历文件列表,逐个添加到ZIP文件中
for _, filePath := range filePaths {
if err = addFileToZip(zipWriter, filePath); err != nil {
return "", err
}
}
return zipFilePath, nil
}
// 将文件写入zip压缩器
func addFileToZip(zipWriter *zip.Writer, filePath string) error {
// 打开要添加的文件
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
// 获取文件信息
fileInfo, err := file.Stat()
if err != nil {
return err
}
// 创建ZIP文件中的文件头
zipHeader, err := zip.FileInfoHeader(fileInfo)
if err != nil {
return err
}
// 将文件头写入ZIP文件
writer, err := zipWriter.CreateHeader(zipHeader)
if err != nil {
return err
}
// 将文件内容复制到ZIP文件中
_, err = io.Copy(writer, file)
if err != nil {
return err
}
return nil
}
效果如下:
参考链接
一文搞懂Go读写Excel文件_go excel-CSDN博客
Golang 生成压缩文件教程_golang 压缩文件-CSDN博客
golang 实现多文件压缩下载_golang将多个文件压缩到一起-CSDN博客