ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ABAP生成带样式Excel:XCO_CP_XLSX从入门到踩坑实践

ABAP生成带样式Excel:XCO_CP_XLSX从入门到踩坑实践 上个月我在维护一张物资统计报表业务方提了个很具体的要求别再用之前那种打开还要选区、分列的“伪 Excel”要一个带红色表头、黄色合计行的 .xlsx 文件最好打开就能直接打印。我当时第一反应是接着用老的 CSV 方案但仔细想了想CSV 根本扛不住这种样式控制的需求。于是把 ABAP 端生成 Excel 的方案整体换成了 XCO_CP_XLSX从写入访问到样式控制一点点摸了一遍。这篇就把整个过程中的使用心得、代码思路和踩坑记录整理出来给同样在 SAP 里跟 Excel 较劲的同行做个参考。接触过 XCO_CP_XLSX 的人应该知道它在 ABAP 里生成 xlsx 并不只是“填数据”而是把工作簿结构、单元格内容、样式定义都作为对象树来构建。换句话说你写的不再是一行行字符串而是以 XCO 框架的方式先定义“这个 Excel 长什么样”再交给序列化逻辑落盘。这篇文章不会只讲 API 清单我会尽量说清楚每一步为什么这么设计以及在实际项目中哪些地方容易翻车。1. 为什么舍 CSV 和 CL_XLSX_DOCUMENT改用 XCO_CP_XLSX先说明白一个前提ABAP 里导出 Excel 从来不是没有方案而是方案太多、各有各的别扭。老一点的经历大概都是这么过来的先是用 GUI_DOWNLOAD 把内表拼成逗号分隔的文本后来系统升级到能支持 XLSX 了又开始用 CL_XLSX_DOCUMENT 这个类再后来才接触到 XCO 库里的 XCO_CP_XLSX。1.1 CSV 导出能交差但离“好用的 Excel”差很远CSV 方案本质是拼文本写法很直接内表转成字符串、带上分隔符、再 DOWNLOAD 到本地就行。但问题在三个地方一是中文乱码问题。SAP 服务器端生成的文本代码页跟 Excel 客户端默认的 UTF-8 经常对不上客户打开后表头一片乱码是家常便饭。后来我知道可以加 BOM或者用 UTF-8 编码下载但每次都要专门处理。二是数据类型全部丢失。数字在 CSV 里就是文本金额列没有千分位、没有小数位控制日期列导过去之后 Excel 不一定认识客户想筛选、求和都得先手工转换格式。三是完全谈不上样式控制。列宽、表头加粗、背景色、边框、合并单元格CSV 一个都做不了。业务方嘴上说“能导出就行”拿到手之后很快就会开始提“能不能好看一点”这时候 CSV 方案就彻底到头了。1.2 CL_XLSX_DOCUMENT能写 xlsx样式却像手工缝补后来 ABAP 升级到能用 CL_XLSX_DOCUMENT 这套老 API 了确实能生成真正的 xlsx 文件也能做基本的单元格设置。但用过的都知道这套接口的痛点在于样式操作非常细碎。你要给标题行加粗得先创建一个单元格对象再对单元格对象设置 font设置底色又是另一套方法合并单元格又要单独处理。假设一张报表有 10 列、表头有 3 行光是让表头变得能看代码就能写几十行。而且这套 API 没有“样式复用”的概念每个单元格的格式都得各自维护写出来的代码既长又难改。更麻烦的是CL_XLSX_DOCUMENT 生成的 xlsx 在打开时偶发兼容性提示。有一回客户反馈“打开文件提示格式有问题”我查了半天最后发现是其中某个单元格塞入了特殊字符导致 XML 结构不干净。这种问题排查起来非常头疼。1.3 XCO 的定位以面向对象的方式描述整个工作簿XCO_CP_XLSX 给人的第一感觉就是“专业”。它不再是逐个单元格地硬塞属性而是让你先定义工作簿、工作表、选区、样式这些高层对象再用一套链式构建器把内容填进去。打个比方CL_XLSX_DOCUMENT 像拿着砖头一块块砌墙而 XCO_CP_XLSX 更像是在图纸上画好墙的位置和样式然后一次性浇灌成型。特别是在样式控制上XCO 可以集中定义几个样式对象再在多个单元格上反复引用生成的文件结构干净代码也短很多。这也是我最终决定切换的根本原因XCO 把“写入访问”和“样式控制”这两件事拆得很清楚写数据是写数据管样式是管样式两者不缠在一起后续要改任何一个维度都很方便。2. 先跑通最小示例从写入访问到第一个 xlsx 文件讲再多原理不如先跑通一个能生成 xlsx 的最小示例。这一步让我理解了 XCO 的 IO 模型也为后面的样式控制打了基础。2.1 环境要求不是所有 ABAP 系统都有 XCO先说环境。XCO_CP_XLSX 属于 XCO 库的一部分XCO 全称是 eXtensibility Change Objects是 SAP 在较新版本 ABAP 平台上推出的对象读写框架。所以第一件事就是确认你的系统里有没有 XCO_CP_XLSX 这个类。我的经验是S/4HANA 2020 之后的版本基本都自带ECC 或者早期版本大概率没有。判断方法很简单SE24 里直接输入 XCO_CP_XLSX 查一下如果类存在直接用就可以。如果不存在那只能退回老方案或者考虑其他生成方式。另外建议在自定义程序中先把这个类放在“较新语法”的代码路径里测试因为 XCO 的 API 大量使用链式调用和构造器老版本 ABAP 的语法兼容性未必跟得上。2.2 write_access 的获取一次打开、反复修改XCO_CP_XLSX 的写入口是 write_access不是直接去 new 一个文件对象而是通过XCO_CP_XLSXWRITE_ACCESS-FOR_FILE( lv_file )这种方式传入一个目标文件路径获得一个“写入访问句柄”。这里有个理解重点这个句柄并不是一打开就往磁盘写数据它更像一个“待提交的工作簿对象”。你可以反复向它添加工作表、修改单元格内容、调整样式在最终调用 write 方法之前磁盘上并不会出现这个文件。这种设计和数据库事务有点像好处是避免了对同一个文件做几十次物理写入也让你能在内存里把整个工作簿组织好再一次性落地。示例代码如下DATA(lv_file) /usr/sap/upload/demo.xlsx. DATA(lo_write_access) XCO_CP_XLSXWRITE_ACCESS-FOR_FILE( lv_file ). DATA(lo_worksheet) lo_write_access-ADD_WORKSHEET( |SHEET1| ).这一小段做的事情很简单拿到文件句柄创建一个名为 SHEET1 的工作表。到这里文件还没有写入只是工作簿里有了一张“空白表”。2.3 工作表内容填充以 builder 方式把行和单元格垒起来有了工作表下一步就是往里填内容。XCO 的惯例不是直接用“A1 单元格 某某值”这种地址式写法而是用 builder 方式一层层构建内容树begin_rows 开启行定义区add_row 添加一行行内部再通过 cells 区域添加单元格end_rows 结束行定义区一个包含表头和一行数据的完整最小示例大致是这样的结构lo_worksheet-SET_WORKSHEET_CONTENT( XCO_CP_XLSX_WORKSHEET_CONTENT-PLAIN( )-BEGIN_ROWS( XCO_CP_XLSX_ROWS-NEW( )-ADD_ROW( XCO_CP_XLSX_ROW-NEW( )-BEGIN_CELLS( )-ADD_CELL( XCO_CP_XLSX_CELL-NEW( IV_VALUE |物料名称| ) ) -ADD_CELL( XCO_CP_XLSX_CELL-NEW( IV_VALUE |数量| ) ) -END_CELLS( ))-ADD_ROW( XCO_CP_XLSX_ROW-NEW( )-BEGIN_CELLS( )-ADD_CELL( XCO_CP_XLSX_CELL-NEW( IV_VALUE |螺母| ) ) -ADD_CELL( XCO_CP_XLSX_CELL-NEW( IV_VALUE 200 ) ) -END_CELLS( )) )-END_ROWS( ) ).代码看着嵌套多但只要记住“先开区、再加项、最后收区”的节奏就不会乱。PLAIN( )表示内容使用普通样式模板后面讲到样式控制时会在这个基础上扩展。2.4 落盘write 之前所有操作都在内存对象树上内容填充完最后一步是落盘lo_write_access-WRITE( ).调用这行之后XCO 才把整个内存对象树序列化成 xlsx 文件写到 FOR_FILE 指定的路径下。我在第一次测试时把 write 漏了程序也不报错结果路径下一直没生成文件排查了好一会儿才反应过来。这个顺序特别容易忽略建议形成肌肉记忆拿到 write_access 之后所有操作都在内存最后一定要主动 write。还有一个细节FOR_FILE 的路径是应用服务器上的路径不是用户本地电脑的路径。如果你是要把文件下载到前端 PC那 write 之后还需要用 cl_gui_frontend_services 的二进制方式把服务器文件传到本地。这个我在后面的坑里会专门展开。3. 样式控制的玩法样式表先行单元格按需引用标题里专门提到“样式控制”可见这是 XCO_CP_XLSX 相对老方案最大的优势。但我第一次用的时候也犯过“老思维”的错误想着怎么给某个单元格设置背景色、给某个单元格设置字体。后来才明白XCO 的样式模型完全不是这个思路。3.1 把样式理解成“CSS”而不是“单元格属性”如果做过前端理解 XCO 样式几乎不用学。它和你写 CSS 一样先定义一个样式类再把单元格“应用”上这个样式而不是每个单元格各自写一堆内联属性。这种集中定义样式的好处非常明显。第一代码里样式定义是一段独立的模块业务方调整颜色、字号只改一段不用在几十个地方逐个找。第二生成的 xlsx 内部结构也更合理因为 Excel 本身存储样式时也是按“共享样式”组织的多个单元格引用同一个样式文件体积能压下来。我建议在动手写代码之前先定义好整个报表需要用到的几类样式表头样式加粗、白字、深色底、居中数据区样式普通字体、细边框、右对齐金额合计行样式加粗、浅黄底、上边框标题样式大号字、跨列合并这几类样式定义完后面的行填充就纯粹是“填数据 引用样式”逻辑干净多了。3.2 报表头部的完整样式加粗、底色、字体颜色与合并以表头样式为例我给一个实际能参考的写法思路。XCO 里样式对象可以通过链式组合多个维度的设置比如字体、填充、边框、对齐等。假设我要定义“表头样式”核心行为是字体加粗字体颜色白色背景色深蓝水平居中、垂直居中细边框对应到代码结构大致如下DATA(lo_header_style) XCO_CP_XLSX_STYLE-NEW( ). lo_header_style-SET_FONT( XCO_CP_XLSX_FONT-NEW( IV_BOLD ABAP_TRUE IV_COLOR |FFFFFFFF| ) ). lo_header_style-SET_FILL( XCO_CP_XLSX_FILL-SOLID( IV_COLOR |FF1F4E78| ) ). lo_header_style-SET_BORDER( XCO_CP_XLSX_BORDER-NEW( IV_SIDE XCO_CP_XLSX_BORDER_SIDEALL IV_STYLE XCO_CP_XLSX_BORDER_STYLETHIN IV_COLOR |FFBFBFBF| ) ).颜色值写法参考了 HTML 的十六进制但开头保留了 FF 这个 Alpha 通道位对应 Excel 内部 ARGB 格式。这个细节是我查了好几个样例才确认的直接写 FFFFFF 在某些版本上不生效加上前两位 FF 才是完整的 ARGB。定义好样式之后在填充行时给对应单元格挂上-ADD_CELL( XCO_CP_XLSX_CELL-NEW( IV_VALUE |物料名称| IV_STYLE lo_header_style ) )这里要注意多个单元格可以共同引用同一个 lo_header_style 对象不是每个单元格都去 new 一个新样式。合并单元格的需求也很常见比如报表大标题要横跨整张表。做法是先填入标题值再对跨列区域做 merge。使用上也是通过选区方式把 A1 到 F1 这个区域合并成一个单元格。合并之后文本居中效果才像样。3.3 正文区的显示细节数字格式、对齐、自动换行与边框正文区的样式比表头更容易被忽略但客户恰恰最在意这里。金额列要千分位、日期列要用 YYYY-MM-DD、长文本列要自动换行、数字列要右对齐。这些在 Excel 里是“格式”在 XCO 里就是数字格式和单元格对齐设置。数字格式很关键经常有人忽略了它导致数字导出后看起来没问题双击却是文本。给单元格指定的 number format 字符串可以直接复用 Excel 的格式语法比如#,##0.00表示千分位加两位小数YYYY-MM-DD表示日期格式。对齐方面数据区默认可能是靠左靠底报表看起来不够整齐。我的建议是数字类列统一右对齐文本类列统一左对齐表头统一居中这样视觉上没有违和感。自动换行则主要用于备注或描述列别让长文本把行高撑破同时也需要在行内容里允许行高自动调整否则换行文本会被截断。边框是“能不能直接打印”的分水岭。很多报表导出后没有边框客户打出来以后跟草稿一样。给数据区统一加个细边框打印效果会专业很多。这里用到的还是 style只不过是一个普通数据样式全部行引用同一个实例。3.4 列宽与行高让导出结果不再需要手工拉宽样式控制不能只盯着单元格本身列宽和行高同样属于“打开即用”的一部分。XCO 里列宽通过列对象来设置。示例中如果第一列是物料名称设置 30 左右的宽度第二列数量设置 15第三列金额设置 20整体就不需要客户再去拖宽。对于自适应用途的长文本列宁可设置富余一点也别因为太窄导致表头折行。行高方面表头行设置得高一点比如 30看起来更大气数据行保持默认就好了。如果业务方要求“打印一页放不下”再回过头调整列宽和行高但至少我们有了控制的入口而不是交付一个默认状态的 Excel。4. 数据量上来之后性能分水岭与应对策略写 Excel 的业务往往一开始是小数据量后面某天突然让你导出全年所有物料问题就来了。XCO_CP_XLSX 虽然写起来方便但它毕竟是在 ABAP 内存里构建整个对象树数据量上去之后性能表现需要认真对待。4.1 1 万行以内的常规导出放心用如果单表导出行数在 1 万以内XCO_CP_XLSX 的体验是最好的。生成时间基本在 1 到 3 秒文件体积可控内存占用也不会报警。在这个量级下我甚至建议不用想太多优化直接把内表循环每行构建一个 XCO 行对象、添加单元格最后一次性 write。代码清晰维护成本低性能完全够用。只需要注意一点不要在循环里每行都 new 一个样式对象全局定义好、在循环里复用即可。4.2 5 万到 10 万行关注内存与文件膨胀超过 5 万行之后XCO 脚本性能会开始出现肉眼可见的变化。主要原因在于每一行、每个单元格都是内存对象对象之间还有引用关系序列化时还要处理样式索引。行数越多对象树越庞大内存占用和生成时间都会超线性增长。我做过一次 8 万行导出测试数据本身不大但每一行都复制了一个新的样式对象结果生成时间接近 20 秒文件也莫名其妙到了 20 多 MB。改成全局共用样式后时间降到 8 秒左右文件缩到 6 MB。这个对比非常直观也说明 XCO 的性能瓶颈很大程度上是“样式对象滥用”导致的。如果必须导出 5 万行以上我有几个建议尽量只导出用户真正需要筛选的字段避免大字段参与对象构建样式定义收敛到个位数不要每个单元格独立带样式大数据量时避免使用跨行合并之类的复杂结构检查服务器配置的内存限制后台任务里导大数据更稳妥4.3 更大数据量不是 XCO 的错是选型的问题超过 10 万行甚至几十万行我就不太推荐 XCO 了。不是说它不行而是任何“在内存中完整构建 Excel 对象树”的方案都会有天花板。这种时候我一般会和其他工具配合比如在应用服务器上用 Python 脚本处理数据生成 xlsxABAP 只负责把数据源传递出去。还有一个现实选择如果业务方对样式要求不高20 万行以上的报表直接用 CSV 反而更实用至少 Excel 打开不卡。所以XCO 适合“对样式有要求、数据量中等”的场景而不是所有导出场景的银弹。选型这件事要在需求阶段就想清楚。5. 我踩过的坑和排查思路最后这部分是我最想写的。XCO 文档里很多用法看一遍就会真正折磨人的是那些环境问题、编码问题、文件占用问题。我把自己踩过比较有代表性的几个坑整理出来希望对大家有帮助。5.1 文件被占用导致写入失败第一次在正式目录里跑程序一段时间后突然报错说文件无法写入。我第一反应是权限问题后来检查发现是因为我测试时用 Excel 打开了那个文件一直没关。Excel 会锁定文件句柄SAP 进程去覆盖同一个路径时就会失败。后续我的习惯是write 到正式路径之前先写一个带时间戳的临时文件确认成功后传给下游或者每次导出前先尝试删除旧文件删除失败就说明文件被占用直接给用户提示。避免让 ABAP 服务器在同一个文件上反复折腾。5.2 下载后提示“文件格式或扩展名无效”这个提示几乎是 ABAP 导出 Excel 的经典问题了。很多时候文件本身没有任何问题问题出在从服务器传输到前端的环节。如果用了纯文本方式下载xlsx 的二进制内容会被转码文件头损坏Excel 自然不认。解决方法是下载时必须走二进制传输。我记得自己当时用 cl_gui_frontend_servicesgui_download 时为了让中文正常把 codepage 设成了 UTF-8结果 xlsx 文件头也跟着被改了。后来我才知道xlsx 不需要文本编码stream 模式二进制传输才是正确姿势。这个细节不踩一次很难记住。5.3 中文乱码分清服务器端和前端两个环节乱码问题要分成两个位置看。第一个是服务器端生成的内容本身是否有乱码第二个是前端下载工具是否按正确编码传输。如果内容本身没有问题那就是前端编码环节出了问题。xlsx 内部的字符串是 Unicode 存储的只要文件结构正确Excel 打开不会乱码。所以我遇到乱码时会先在服务器上用记事本检查 xlsx 是否存在如果服务器端文件打开正常那问题就在下载环节优先检查 binary 方式和前端工具版本。5.4 样式重复声明文件几十 MB 的隐患前面性能部分提到过导出 8 万行时每个单元格一个样式文件直接 20 多 MB。这个问题的本质是 xlsx 内部存储样式时要写大量重复的 XML而 XCO 只是把所有单元格引用的样式信息如实序列化了。解决方法是严格用“共享样式”思路先定义一个样式池业务需要的样式类型控制在一定数量内单元格只存样式索引。这样文件大小和内存占用都能大幅下降。我给团队分享这条经验时经常说一句话在 Excel 文件里重复的样式不是样式是垃圾。5.5 版本上没有 XCO 时的退路如果系统确实没有 XCO 库我也试过几种替代方案。最常用的还是 CL_XLSX_DOCUMENT虽然样式控制繁琐但至少能生成 xlsx。如果想省事也可以生成“带 BOM 的 CSV”让客户在 Excel 里直接打开。还有一个思路是 ABAP 调用外部生成服务比如把数据写成 JSON 或 XML再触发应用服务器上的脚本转换。这个适合数据量特别大又必须 xlsx 格式的场景。总之XCO 是好工具但不是所有环境都有平时多准备几条退路没有坏处。在这个项目之后我把团队里大多数报表导出都切换到了 XCO_CP_XLSX也沉淀了一个公共方法传入内表、列定义和样式配置出去的就是一个可用的 xlsx。现在再接导出需求我会先画清楚样式的“设计稿”再落代码因为 XCO 的 builder 结构其实和你脑子里的表格布局是一一对应的设计越清楚代码就越不需要返工。最后再分享一个小技巧调试阶段把 write 目标指向临时目录每改一步样式就打开文件看一眼确认无误后再切换到正式路径这个过程能省下大量整体排错的时间。
返回列表