最近在公司了个需求,主要涉及到文件导出,需要根据特定表格文件生成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文件1f1 := excelize.NewFile() // 在Excel中创建一个新的sheet页f1.NewSheet("Sheet1")// 向工作表中写入数据if err = f1.SetCellValue("Sheet1", "A1", "Hello, World!"); err != nil {return}// 创建一个新的Excel文件2f2 := 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 mainimport ("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 // TODOreturn
}
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博客