package excel_export import ( "encoding/json" "fmt" "gitea.cdlsxd.cn/self-tools/l_excel_export/export_err" "gitea.cdlsxd.cn/self-tools/l_excel_export/pkg" "gitea.cdlsxd.cn/self-tools/l_excel_export/types" "github.com/xuri/excelize/v2" "io" "math" "net/http" "os" "sort" "time" ) type ExportExcel struct { config *Config password string saveFunc func(file *excelize.File) (url string, err error) sheetName string logPath string jobName string task *types.Task exportData [][]interface{} header []interface{} } func NewExport(opts ...Option) *ExportExcel { e := &ExportExcel{} for _, opt := range opts { opt(e) // 应用选项 } e.initJobName() return e } func (e *ExportExcel) Run(config *Config) (taskId string, err error) { err = e.init(config) if err != nil { return } f, err := e.getFile() if err != nil { return } go func(f *excelize.File) { defer func() { if r := recover(); r != nil { f.Close() fmt.Println(r) } }() e.task.Status = Running e.updateTask() err = e.run(f) if err != nil { e.task.Status = Err e.updateTask() return } e.task.Ftime = time.Now().Format(time.DateTime) e.finish(f) }(f) return e.task.TaskId, nil } func (e *ExportExcel) TaskInfo(task_id string) (task *types.Task, err error) { e.logPath, err = pkg.DefaultLogPath(e.jobName) if err != nil { return nil, err } e.task = &types.Task{ TaskId: task_id, } taskInfo, err := os.ReadFile(e.logFile()) if err != nil { return nil, err } _ = json.Unmarshal(taskInfo, e.task) return e.task, nil } func (e *ExportExcel) DownloadExcel(w http.ResponseWriter, task_id string) (err error) { task, err := e.TaskInfo(task_id) if err != nil { return err } _, exist := os.Stat(task.Url) if exist != nil { return fmt.Errorf("文件不存在") } file, err := os.Open(task.Url) if err != nil { return err } defer file.Close() payload, err := io.ReadAll(file) if err != nil { return err } // 设置HTTP响应头 // 打开为预览 //ctx.Response().Header().Set("Content-Type", "image/png") // 打开为下载 w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Disposition", "attachment; filename="+task.Url) // 将结果写入 _, err = w.Write(payload) return } func (e *ExportExcel) TaskHis(page int, num int) (res *types.ResPage, err error) { var data []*types.Task e.logPath, err = pkg.DefaultLogPath(e.jobName) if err != nil { return res, err } entries := e.SortLogFileWithStatus() count := len(entries) begin := (page - 1) * num entEnd := begin + num if count < entEnd { entEnd = count } data = entries[begin:entEnd] return &types.ResPage{ Page: page, Limit: num, Total: count, Data: data, LastPage: int(math.Ceil(float64(count) / float64(num))), }, nil } func (e *ExportExcel) run(f *excelize.File) (err error) { index, err := f.NewStreamWriter(e.getSheetName()) if err != nil { return err } index.SetRow("A1", e.header) if err != nil { return err } count := len(e.exportData) for i, v := range e.exportData { cell, _ := excelize.CoordinatesToCellName(1, i+2) index.SetRow(cell, v) e.task.Process = i * 100 / count e.updateTask() } if err = e.save(f); err != nil { return err } return err } func (e *ExportExcel) finish(f *excelize.File) { f.Close() e.task.Status = Finish e.task.Process = 100 e.updateTask() } func (e *ExportExcel) save(f *excelize.File) error { e.task.Url = e.getUrl() return f.SaveAs(e.task.Url) } func (e *ExportExcel) getUrl() string { return fmt.Sprintf("%s/%s_%s%s", e.config.SavePath, e.config.FileName, e.task.TaskId, e.config.Ext) } func (e *ExportExcel) init(config *Config) error { e.config = config err := e.check() if err != nil { return err } e.task = &types.Task{ TaskId: pkg.CreateTaskId(), Process: 0, Url: "", Ctime: time.Now().Format(time.DateTime), Ftime: "", Status: Init, } err = e.updateTask() if err != nil { return err } dataMap := pkg.GetData(e.config.Data) if len(dataMap) == 0 { return export_err.ErrDataError } if len(e.config.Head) == 0 { return export_err.ErrHeadNotSet } for _, v := range e.config.Head { e.header = append(e.header, v.ColName) } //todo:这一步目的是为了保证数据与header未知一致,但是在大数据量的时候存在性能问题 for _, v := range dataMap { var ( slice []interface{} ) for _, vv := range e.config.Head { slice = append(slice, v[vv.FieldName]) } e.exportData = append(e.exportData, slice) } //检测文件是否存在 if _, err = os.Stat(e.config.SavePath); os.IsNotExist(err) { // 文件夹不存在,尝试创建 err = os.MkdirAll(e.config.SavePath, os.ModePerm) if err != nil { return export_err.ErrCreateExcelPathFail } } else if err != nil { return export_err.ErrCheckExcelPathFail } return nil } func (e *ExportExcel) updateTask() error { file, err := os.OpenFile(e.logFile(), os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0766) if err != nil { return err } defer file.Close() taskInfo, err := json.Marshal(e.task) if err != nil { return err } jsonInfo := string(taskInfo) _, err = file.WriteString(jsonInfo) if err != nil { return err } return nil } func (e *ExportExcel) logFile() string { return fmt.Sprintf("%s/%s", e.logPath, e.task.TaskId) } func (e *ExportExcel) getFile() (*excelize.File, error) { f, err := excelize.OpenFile(e.path()) if err != nil { f = excelize.NewFile() } return f, nil } func (e *ExportExcel) path() string { return fmt.Sprintf("%s/%s.%s", e.config.SavePath, e.config.FileName, e.config.Ext) } func (e *ExportExcel) check() (err error) { if e.config.SavePath == "" && e.saveFunc == nil { return export_err.ErrNotSetSaveWay } if len(e.logPath) == 0 { e.logPath, err = pkg.DefaultLogPath(e.jobName) return } return } func (e *ExportExcel) initJobName() { if len(e.jobName) == 0 { e.jobName = "default" } } func (e *ExportExcel) getSheetName() string { if len(e.sheetName) == 0 { e.sheetName = "Sheet1" } return e.sheetName } func (e *ExportExcel) SortLogFileWithStatus() (fileInfoList []*types.Task) { // 获取目录中的文件信息 d, _ := os.Open(e.logPath) defer d.Close() files, _ := d.ReadDir(0) // 填充切片 for _, file := range files { fileName := file.Name() bytes, _ := os.ReadFile(e.logPath + "/" + fileName) var info types.Task _ = json.Unmarshal(bytes, &info) fileInfoList = append(fileInfoList, &info) } // 根据修改时间对切片进行排序 sort.Slice(fileInfoList, func(i, j int) bool { return fileInfoList[i].Ctime >= fileInfoList[j].Ctime }) return fileInfoList }