导出Excel,从PHP下xlswriter到golang的进化是2分缩减到5秒

先看图

 

数据条数:9000+ 

第1,2行,golang+excelize方式导出,耗时:5s

第3行,PHP+xlswriter方式导出,耗时:2min

一、介绍

xlswriter是一个高效处理excel文件的PHP扩展,底层以C语言实现;处理速度是PHPExcel几十倍甚至几百倍的效率。

官方链接:https://gitee.com/viest/php-ext-xlswriter

缺点:更深入的功能(例如读取excel图片)不健全;导出excel样式不够丰富

导出excel文件,xlswriter绝对是效率最高的处理方式,配合php后台任务导出百万千万级别数据也没有问题

通用导出类:

class ExportOperateAnaly
{
    public $export_limit = 13000;

    /**
     * 统一导出
     * @Author   zhibin3
     * @DateTime 2022-12-24
     * @param    array      $pageData [description]
     * @return   [type]               [description]
     */
    public function layout(array $params, $is_export=true)
    {
        ini_set('memory_limit', '512M');
     $list = [];
        //todo
        $header_list = ['商品ID', '商品标题', '商品图片'];
        $width_list = ['商品图片'=>55,'商品标题'=>55];

        return $this->export($header_list, $list, '商品运营分析', $is_export, $width_list);
    }

    /**
    * 通用导出方法
    * @Author   zhibin3
    * @DateTime 2023-02-14
    * @param    array      $header_list [description]
    * @param    array      $list        [description]
    * @return   [type]                  [description]
    */
   public function export(array $header_list, array $datas, $filename='选款导出列表', $is_export=true, array $width_list=[])
   {
        $config = [
            'path' => ROOT_PATH . 'public' . DS . 'dl' // xlsx文件保存路径
        ];
        $Excel = new \Vtiful\Kernel\Excel($config);

        $filename .= datetime(time(), 'Y-m-d_H_i_s').'.xlsx';
        $fileObject = $Excel->fileName($filename, 'sheet1')->header($header_list);
        $fileHandle = $fileObject->getHandle();

        $format    = new \Vtiful\Kernel\Format($fileHandle);
        $boldStyle = $format->bold()->toResource();
        $alignStyle = $format->align(\Vtiful\Kernel\Format::FORMAT_ALIGN_CENTER, \Vtiful\Kernel\Format::FORMAT_ALIGN_VERTICAL_CENTER)->toResource();
        // $backgroundStyle  = $format->background(0xD9D9D9)->toResource();

        foreach ($datas as $index => $val) {
            $column = 0;
            foreach ($val as $key => $value) {
                $row = $index+1;
                /*if ($key=='img') {
                    $return = ImageImport::export_image_save($value);
                    $img_path = $return['image'] ? $return['path'] : $value;
                    // pre($value, $return['image'], $row, $column, $img_path);
                    $fileObject->insertImage($row, $column, $img_path, 0.1, 0.1);
                }*/

                if ($key=='lowest_price') {
                    $fileObject->insertFormula($row, $column, $value);
                }
                else if (is_string($value)) {
                    $fileObject->insertText($row, $column, $value);
                } else {
                    $fileObject->insertText($row, $column, $value);
                }

                $column++;
            }
        }
        // pre($datas);die;
        
        foreach ($header_list as $key => $value) {
            // $letter = $header_map[$key];
            $letter = \Vtiful\Kernel\Excel::stringFromColumnIndex($key);
            $width_value = $width_list[$value] ?? 20;
            $fileObject->setRow($letter.($key+1), 35, $boldStyle);
            $fileObject->setColumn($letter.':'.$letter, $width_value, $alignStyle);
        }

        $format2    = new \Vtiful\Kernel\Format($fileHandle);
        $alignStyle = $format2->align(\Vtiful\Kernel\Format::FORMAT_ALIGN_CENTER, \Vtiful\Kernel\Format::FORMAT_ALIGN_VERTICAL_CENTER)->toResource();
        $wrapStyle = $format2->wrap()->toResource();
        foreach ($datas as $index => $value) {
            $row = $index+2;
            $fileObject->setRow('A'.$row, 30, $alignStyle);  
        }
        // pre($fileObject);die;
        $filePath = $fileObject->output();
        if ($is_export) {
            header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            header('Content-Disposition: attachment;filename="' . $filename . '"');
            header('Content-Length: ' . filesize($filePath));
            header('Content-Transfer-Encoding: binary');
            header('Cache-Control: must-revalidate');
            header('Cache-Control: max-age=0');
            header('Pragma: public');

            if (ob_get_contents()) ob_clean();
            flush();
            if (@copy($filePath, 'php://output') === false) throw new \Exception($filePath. '地址出问题了');
            @unlink($filePath);

            return ['code'=>1, 'msg'=>'导出成功', 'file_path'=>''];
        } else {
            
            return ['code'=>1, 'msg'=>'导出成功', 'file_path'=>'dl/'.$filename];
        }   
    }

}

二、golang

   虽然xlswriter在PHP中是比较高效的excel文件处理扩展,但是跟golang相比还是比较大的距离;这就是解释语言与编译语言差距。

   golang中使用excelize处理excel文件

  具体:github.com/xuri/excelize/v2

  数据:gorm.io/driver/mysql + gorm.io/gorm

  通用导出处理:

  

  1 package services
  2 
  3 import (
  4     "fmt"
  5     "gin_async/models"
  6     "gin_async/utils"
  7     "gin_async/config"
  8     "github.com/xuri/excelize/v2"
  9     "reflect"
 10     "strings"
 11     "time"
 12     "strconv"
 13 )
 14 
 15 var OperateProductSaleService = new(OperateProductSale)
 16 
 17 type OperateProductSale struct {
 18 }
 19 
 20 func (s *OperateProductSale) Startup(data utils.Dict, logId int, isExport bool) {
 21    //todo
 22     //fmt.Println(list) return
 23     var headerList = []string{"商品ID", "商品标题", "商品图片", "商品链接", "店铺名称", "款号", "运营", "一级分类", "二级分类", "销量", "销售额", "发货金额", "退款金额", "退货金额", "净销售额", "14天退款率", "推广费用", "履约费用", "公摊费用", "商品成本", "退货商品收入", "佣金及服务费", "分账及扣款", "其他服务费", "预估毛利润", "预估净利润", "订单量", "发货前退货", "库存", "曝光", "点击", "点击率", "点击成交率", "收藏"}
 24     var widthMap = map[string]float64{
 25         "商品ID": 16,
 26         "商品标题": 30,
 27         "商品图片": 30,
 28         "商品链接": 30,
 29     }
 30 
 31     filename, err := s.Export(headerList, rows, widthMap)
 32     var code int
 33     var msg string
 34     if err != nil {
 35         code = 0
 36         msg = "导出文件错误"
 37     } else {
 38         code = 1
 39         msg = "导出成功"
 40     }
 41     models.AsyncLogModel.UpdateLog(logId, map[string]interface{}{"code": code, "msg": msg, "file_path": filename, "remark":"from gin export"}, filename)
 42     fmt.Println("异步任务[export_operate_analy]结束,时间:", time.Now().Format("2006-01-02 15:04:05"))
 43 }
 44 
 45 var colIndex int     //定义全局列属性
 46 var rowIndex int     //定义全局行属性
 47 var f *excelize.File //定义全局文件属性
 48 var Sheet1 string
 49 
 50 func (s *OperateProductSale) Export(headerList []string, list []models.GetRelatProductStruct, widthMap map[string]float64) (string, error) {
 51     savePath := config.GlobalConfig.Common.SavePath //读取配置文件
 52     if savePath=="" {
 53         savePath = "./"
 54     }
 55 
 56     //fmt.Println(widthMap)
 57     f = excelize.NewFile()
 58     defer func() {
 59         if err := f.Close(); err != nil {
 60             fmt.Println(err)
 61         }
 62     }()
 63 
 64     Sheet1 = "Sheet1"
 65     // 创建一个工作表
 66     index, err := f.NewSheet(Sheet1)
 67     if err != nil {
 68         fmt.Println(err)
 69         return "", err
 70     }
 71 
 72     // 创建一个样式,设置字体大小为14
 73     style, err := f.NewStyle(&excelize.Style{
 74         Font: &excelize.Font{
 75             Size:  13,
 76             Bold:  true,
 77             Color: "#000000",
 78         },
 79         Alignment: &excelize.Alignment{
 80             Horizontal: "center",
 81             Vertical:   "center",
 82         },
 83     })
 84     if err != nil {
 85         fmt.Println(err)
 86         return "", err
 87     }
 88 
 89     // 设置单元格的值
 90     //var lastCol string
 91     //var headerListLen = len(headerList)
 92     for index, header := range headerList {
 93         cellAddress, _ := excelize.CoordinatesToCellName(index+1, 1)
 94         /*if index == headerListLen-1 {
 95             lastCol = cellAddress
 96         }*/
 97 
 98         f.SetCellValue(Sheet1, cellAddress, header)
 99         f.SetCellStyle("Sheet1", cellAddress, cellAddress, style)
100 
101         widthValue, ok := widthMap[header]
102         if !ok {
103             widthValue = 14
104         }
105         err = f.SetColWidth(Sheet1, strings.Trim(cellAddress, "1"), strings.Trim(cellAddress, "1"), widthValue) //设置宽度
106     }
107     //err = f.SetColWidth(Sheet1, "A", strings.Trim(lastCol, "1"), 15) //设置宽度
108     err = f.SetRowHeight(Sheet1, 1, 25) //设置高度
109 
110     //插入数据行
111     for rIndex, value := range list {
112         cost := fmt.Sprintf("%.2f", value.Cost)
113         refundCost := fmt.Sprintf("%.2f", value.RefundCost)
114         //方式一
115         /*values := getStructFieldValues(row)
116         for colIndex, value := range values {
117             if colIndex > headerListLen-1 {
118                 continue
119             }
120             cellAddress, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+2)
121             f.SetCellValue("Sheet1", cellAddress, value)
122         }*/
123 
124         //方式二
125         colIndex = 1
126         rowIndex = rIndex + 2
127         SetValue(value.ShopStyleCode) //商品ID
128         SetValue(value.ItemName)      //商品标题
129         SetValue(value.ItemMainImage) //商品图片
130         SetValue(value.ShopLink)      //商品链接
131         SetValue(value.ShopName)      //店铺名称
132         SetValue(value.LocalStyleCode)
133         SetValue(value.User)
134         SetValue(value.FClassifyName)
135         SetValue(value.SClassifyName)
136         SetValue(value.SaleQty)
137         SetValue(value.SaleAmount)
138         SetValue(value.SendAmount)
139         SetValue(value.RefundAmount)
140         SetValue(value.ReturnAmount)
141         SetValue(value.NetSaleAmount)
142         SetValue(value.RefundRate)
143         SetValue(value.PromoteCost)
144         SetValue(value.PerformCost)
145         SetValue(value.PublicCost)
146         SetValue(cost)
147         SetValue(refundCost)
148         SetValue(value.PayCommissionService)
149         SetValue(value.PayShareDebit)
150         SetValue(value.PayOtherService)
151         SetValue(value.PredictGrossProfit)
152         SetValue(value.PredictProfit)
153         SetValue(value.OrderQty)
154         SetValue(value.UnsendRefundQty)
155         SetValue(value.Stock)
156         SetValue(value.Exposure)
157         SetValue(value.Hits)
158         SetValue(value.HitsRate)
159         SetValue(value.HitsDealRate)
160         SetValue(value.Collect)
161     }
162 
163     // 设置工作簿的默认工作表
164     f.SetActiveSheet(index)
165 
166     datetime := time.Now().Format("2006-01-02_15_04_05")
167     filename := fmt.Sprintf("dl/商品运营分析_%s.xlsx", datetime)
168     savefile := fmt.Sprintf("%s%s", savePath, filename)
169     fmt.Println("保存地址:"+savefile)
170     // 根据指定路径保存文件
171     if err := f.SaveAs(savefile); err != nil {
172         fmt.Println(err)
173     }
174 
175     fmt.Println("Excel file exported successfully.")
176     return filename, nil
177 }
178 
179 func SetValue(value interface{}) {
180     cellAddress, _ := excelize.CoordinatesToCellName(colIndex, rowIndex)
181     f.SetCellValue(Sheet1, cellAddress, value)
182 
183     colIndex++
184 }

 

原文地址:https://www.cnblogs.com/cnxzb/p/18005886

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


如何选择合适的 C++ Web 开发框架?
利用 C++ 框架构建高并发 Web 应用的策略
用 C++ 框架开发跨平台图形应用程序
golang框架中安全编码实践的最佳指南是什么?
golang框架与其他语言框架在设计理念上的区别有哪些?
C++ 图形框架与其他语言框架的比较
C++ 框架与其他 Web 开发框架的对比分析
使用 C++ 框架构建大型项目最佳实践
C++ 框架如何提高大型项目开发效率
C++ 框架中依赖注入的持续集成与部署工具
如何与社区协作和贡献到自定义 Golang 框架?
C++ 框架在大型项目中如何实现模块化开发
使用 C++ 框架开发跨平台 Web 应用
C++ 框架在大型项目中的优缺点
golang框架在性能上的优势体现在哪些方面?
C++ 框架在嵌入式系统内存优化中的优势
golang框架在人工智能与机器学习中的作用
如何扩展 Golang 框架以支持特定功能?
如何利用 Go Modules 和依赖项管理来自定义 Golang 框架?
Golang 框架中的性能优化技巧