ARTICLE DETAIL

资讯详情

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

ABAP实战:内表生成Excel并作为邮件附件发送的完整开发指南

ABAP实战:内表生成Excel并作为邮件附件发送的完整开发指南 1. 需求拆解与技术选型ABAP开发里“把内表数据生成Excel并作为邮件附件发出去”这个需求几乎是每个SAP顾问迟早都会碰到的活儿。财务要报表、物资要库存清单、销售要订单明细、HR要人员名单业务部门永远有一堆“帮我导个表发到邮箱”的需求。我一听就明白这种需求看着简单其实坑不少而且每个企业选型往往直接关系到后续维护成本。先说场景。在我实际接手的项目里这类需求通常分三种形态第一种是报表输出比如用户执行某个T-code屏幕上方显示ALV结果用户自己点“发送邮件”按钮第二种是后台JOB定时跑比如每天凌晨把昨天的销售明细汇总成Excel发到经理邮箱第三种是为了满足外围系统接口比如把SAP数据导出后发给下游平台做二次处理。虽然形态不同但技术栈是完全一样的都是在ABAP里做三件事生成Excel文件、拼接邮件内容、挂附件发出去。技术选型上ABAP里生成Excel常见有三条路OO ALV导出、OLE2直接操作Excel、ABAP2XLSX开源库。我结合实战经验先给结论如果只是把内表数据快速转成Excel发出去OO ALV导出是最稳妥、最少依赖的方案OLE2适合复杂格式合并单元格、图表、多Sheet但服务器上必须安装并配置Excel组件性能差而且SAP GUI登录用户的桌面环境才可能生效后台JOB环境下几乎不可用ABAP2XLSX功能最强生成的是真正的XLSX文件能处理样式、公式、数据透视表但需要额外引入开源代码很多企业IT策略不允许随便引入第三方代码。这篇文章按“不引入任何第三方组件、不依赖服务器Excel环境”的约束来讲所以主线方案就是OO ALV转换内表生成XLS文件再加上标准函数发送邮件。提醒一下如果你是在S/4HANA环境做还要确认系统版本是否支持XLSX生成。老一点的ECC系统用XLS格式没毛病但XLSX在邮件客户端里的兼容性更好。后面我会给一个扩展思路在不依赖ABAP2XLSX的前提下也能输出XLSX。2. 核心技术点内表数据如何变成Excel字节流2.1 首选方案通过OO ALV获取XLS格式内容把内表转成Excel老手基本不会自己用OPEN DATASET一行行写文件那样性能差、格式也丑。我们用的方案走的是ABAP标准对象cl_salv_table或者cl_gui_alv_grid核心思路是先在内存中生成ALV对象然后调用它的转换方法直接把内表“渲染”成Excel格式的字节约流。这套逻辑本质是SAP标准功能在“导出到本地文件”时背地里做的事情我们只是把它截留在内存里转而交给邮件函数。DATA: lt_data TYPE TABLE OF scarr, lo_alv TYPE REF TO cl_salv_table, lo_func TYPE REF TO cl_salv_functional_settings. TRY. cl_salv_tablefactory( IMPORTING r_salv_table lo_alv CHANGING t_table lt_data ). CATCH cx_salv_msg. MESSAGE 生成ALV失败 TYPE E. ENDTRY.这只是生成ALV对象确认能基于内表结构渲染。真正输出Excel内容还需要调用cl_salv_bs_alv的导出服务或者用另一个更直接的方式——函数模块SAP_CONVERT_TO_XLS_FORMAT。这个函数我常用它直接把任意内表和字段描述转换成XLS二进制内容也是很多导出程序的底层实现。DATA: lt_content TYPE solix_tab, lv_size TYPE so_obj_len. CALL FUNCTION SAP_CONVERT_TO_XLS_FORMAT EXPORTING i_filename sales_report.xls i_append_mode space TABLES i_tab_structure lt_data[] i_tab_data lt_data[] CHANGING i_tab_raw_data lt_content[] EXCEPTIONS conversion_failed 1 OTHERS 2.这里有个细节很多人初次接触会困惑i_tab_structure和i_tab_data都传同一个内表没错这个函数就是分别接收“字段结构信息”和“数据行信息”内部统一序列化。如果你要导出的数据有字段文本、列宽等额外要求使用之前先执行一次再判断结果。返回的lt_content是XLS文件的二进制内容不落盘直接进内存干净利索。2.2 备选方案XLSX格式的快速生成思路刚才提到S/4HANA或业务要求更高兼容性时可以考虑输出XLSX。在不引入ABAP2XLSX的前提下有一个取巧又实用的方案XLSX文件本质是ZIP压缩包里面装着多个XML文件。你可以用SAP标准类cl_abap_zip在内存里拼装出一个最小的XLSX文件结构再用cl_abap_browser之类的工具验证。不过这个方案需要手写多个XML模板安装表头和sheet数据非常繁琐代码量大、对新手不友好。我的建议是如果团队内允许引用开源直接用ABAP2XLSX它封装好了所有XML生成逻辑如果不允许就老老实实用XLS格式Excel 2010以后版本打开XLS没有问题只是文件本身大一点、列数限制在256列。实际项目里我见过大量企业到现在还在用XLS附件业务侧根本无感知。3. 核心代码实现发送带Excel附件的邮件3.1 邮件发送函数的选型与参数说明ABAP里发邮件有两条主线老牌函数SO_DOCUMENT_SEND_API1和新的BCSBusiness Communication Service接口。我工作中老项目维护多用SO_DOCUMENT_SEND_API1因为它参数直观、示例多、好排查新项目或要发HTML正文、定制邮件头时偏向BCS。这篇文章选SO_DOCUMENT_SEND_API1为主线因为对“带附件Excel”这个需求来说它最简单直接代码量少。DATA: ls_docdata TYPE sodocchgi1, lt_objpack TYPE TABLE OF solisti1, ls_objpack LIKE LINE OF lt_objpack, lt_objtxt TYPE TABLE OF solisti1, ls_objtxt LIKE LINE OF lt_objtxt, lt_receivers TYPE TABLE OF somlreci1, ls_receiver LIKE LINE OF lt_receivers, lt_attachment TYPE TABLE OF solisti1, lv_sent_to_all TYPE so_sent_to_all. ls_docdata-obj_descr 月度库存报表. ls_docdata-obj_name ZMM_INV_REPORT. ls_docdata-obj_type XLS. ls_docdata-sensitivty C. ls_receiver-receiver managercompany.com. ls_receiver-rec_type U. ls_receiver-com_type INT. APPEND ls_receiver TO lt_receivers.这里前三个字段要重点解释obj_descr是邮件附件显示的文件名不含扩展名obj_name是SAP内部文档名obj_type是文档类型XLS对应Excel文件。注意obj_descr会在邮件附件列表中展示务必写清楚别直接写个ABC之类的名字业务同事收到邮件后都不知道是什么表。sensitivtyC表示普通敏感级别公司内部邮件比较常见可以减少邮件网关误拦截。3.2 组装附件与发送正文上面用SAP_CONVERT_TO_XLS_FORMAT得到的lt_content是solix_tab类型二进制行这里发送函数要求附件行类型是solisti_tab字符串行所以需要转换。实际上solix和solisti在SAP内部表示的都是255字符一行的分块数据只是类型上一个是XSTRING一个是CHAR直接按字节搬移就行。很多第一次做的同事会在这里卡住我先给可运行的转换逻辑DATA: lt_attachment_hex TYPE solix_tab, lv_content_string TYPE string, lv_hex TYPE xstring. * 将solix_tab拼接成xstring再展开为solisti_tab CLEAR lv_hex. LOOP AT lt_attachment_hex INTO DATA(ls_hex_line). CONCATENATE lv_hex ls_hex_line-line INTO lv_hex IN BYTE MODE. ENDLOOP. CALL FUNCTION SCMS_XSTRING_TO_BINARY EXPORTING buffer lv_hex IMPORTING output_length DATA(lv_len) TABLES binary_tab lt_attachment.这一段的逻辑顺序很重要先把每条255字节的hex行拼接成一个整体xstring再用SCMS_XSTRING_TO_BINARY展开成传输行。如果你跳过拼接直接循环行转换那么超过255字节的文件会被截断邮件附件打开就损坏。这个坑我当年踩过后来写进部门SAP开发规范里了所有人都抄这个模板。现在可以正式发送CALL FUNCTION SO_DOCUMENT_SEND_API1 EXPORTING document_data ls_docdata put_in_outbox X commit_work X IMPORTING sent_to_all lv_sent_to_all TABLES object_header lt_objpack object_content lt_objtxt contents_hex lt_attachment receivers lt_receivers EXCEPTIONS too_many_receiveers 1 document_not_sent 2 document_type_not_exist 3 operation_no_authorization 4 OTHERS 5. IF sy-subrc 0. MESSAGE 邮件发送失败 TYPE E. ENDIF.参数里put_in_outboxX表示发送后还要放入SAP发件箱方便追踪。commit_workX代表函数内部提交事务一般保持这个设置否则可能出现邮件实际发出但程序报异常的假象。不要小看这个参数我前后端联调时曾经因为commit_work不设置邮件在SAP端显示“待发”而实际永远发不出去排查半天。3.3 完整核心代码示例含内表组装把上面拆开的逻辑整合一下给你一份可以直接在SE38里创建测试程序运行的完整代码。这里模拟一个销售订单汇总的场景。REPORT z_mail_excel_demo. TYPES: BEGIN OF ty_sales, vbeln TYPE vbeln, 销售订单号 erdat TYPE erdat, 创建日期 kunnr TYPE kunnr, 客户编号 netwr TYPE netwr, 净价值 waerk TYPE waerk, 货币 END OF ty_sales. DATA: lt_sales TYPE TABLE OF ty_sales, lt_hex TYPE solix_tab, lv_xstring TYPE xstring, lt_att_text TYPE solisti_tab. START-OF-SELECTION. * 演示直接构造几行数据实际项目里用SELECT即可 APPEND INITIAL LINE TO lt_sales ASSIGNING FIELD-SYMBOL(fs). fs-vbeln 10000001. fs-erdat sy-datum. fs-kunnr C001. fs-netwr 12000.50. fs-waerk CNY. APPEND INITIAL LINE TO lt_sales ASSIGNING fs. fs-vbeln 10000002. fs-erdat sy-datum. fs-kunnr C002. fs-netwr 5600.00. fs-waerk CNY. * 第一步内表转Excel二进制 CALL FUNCTION SAP_CONVERT_TO_XLS_FORMAT EXPORTING i_filename sales.xls i_append_mode space TABLES i_tab_structure lt_sales[] i_tab_data lt_sales[] CHANGING i_tab_raw_data lt_hex EXCEPTIONS conversion_failed 1 OTHERS 2. IF sy-subrc 0. MESSAGE XLS转换失败 TYPE E. ENDIF. * 第二步hex转传输行 CLEAR lv_xstring. LOOP AT lt_hex INTO DATA(ls_hex). CONCATENATE lv_xstring ls_hex-line INTO lv_xstring IN BYTE MODE. ENDLOOP. CALL FUNCTION SCMS_XSTRING_TO_BINARY EXPORTING buffer lv_xstring IMPORTING output_length DATA(lv_length) TABLES binary_tab lt_att_text. * 第三步发送邮件 PERFORM send_mail USING lt_att_text. FORM send_mail USING pt_att TYPE solisti_tab. DATA: ls_docdata TYPE sodocchgi1, lt_receivers TYPE TABLE OF somlreci1, ls_receiver LIKE LINE OF lt_receivers. ls_docdata-obj_descr 月度销售汇总. ls_docdata-obj_name ZMM_SALES_MONTHLY. ls_docdata-obj_type XLS. ls_docdata-sensitivty C. ls_docdata-no_mass_send X. ls_receiver-receiver sap.testcompany.com. ls_receiver-rec_type U. ls_receiver-com_type INT. APPEND ls_receiver TO lt_receivers. CALL FUNCTION SO_DOCUMENT_SEND_API1 EXPORTING document_data ls_docdata put_in_outbox X commit_work X TABLES contents_hex pt_att receivers lt_receivers EXCEPTIONS too_many_receiveers 1 document_not_sent 2 document_type_not_exist 3 operation_no_authorization 4 OTHERS 5. IF sy-subrc 0. MESSAGE ID sy-msgid TYPE E NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. ENDIF. ENDFORM.这段代码在大多数ECC和S/4HANA系统上都能直接跑通。需要注意几处业务字段的域定义vbeln、erdat这些字段来自标准表VBAK如果你复制的系统里没有这些数据元素可以把类型定义改成字符串CHAR20之类演示效果不受影响。3.4 邮件正文怎么加别只用纯附件很多需求方只要“附件Excel”但实操经验告诉我邮件正文至少写一句话避免被对方邮件网关当垃圾邮件处理。SO_DOCUMENT_SEND_API1的正文内容挂在it_objtxt参数上类型也是solisti_tab每行255字符我们把多行文本拼接进去即可DATA: lt_objtxt TYPE TABLE OF solisti_t1. APPEND 您好 TO lt_objtxt. APPEND 附件为本月销售汇总数据请查收。 TO lt_objtxt. APPEND 如有疑问请联系SAP支持组。 TO lt_objtxt.然后把lt_objtxt传给object_content参数。这里有个先后问题正文行和附件行都叫solisti_tab别传混了。我在代码评审里见过有人把正文内容传成contents_hex结果邮件发出后附件变成一组乱码文本很容易暴露开发经验不足。4. 实操过程完整记录与调试要点4.1 按步骤复现的完整路径我一般建议新人在沙箱环境里一步步来别急着改业务代码。先建一个Z测试程序内表用最简单的单表比如SCARR或自定义结构走通“内表→XLS→邮件→收件箱验证”四步再套进真实业务查询。步骤一SE38建程序插入上面的完整代码把收件人改成自己邮箱。运行前一定用SE36或SE11检查所引用的数据元素是否存在。步骤二运行程序如果一切正常你会看到绿色消息“邮件发送成功”。这时去SAP的SBWP业务工作台收件箱就能看到这条邮件记录双击可以预览内容。这就证明SAP侧已经写入发送队列。步骤三检查外部邮箱是否收到。这里要注意很多企业SAP系统是通过SCC4或SCOT配置的SMTP服务器发信的目标邮箱如果是外部地址还要看邮件网关是否放行。我建议第一步先用公司内部邮箱测排除网络和网关因素。步骤四验证附件完整性。下载附件后分别用Excel 2007兼容模式和Microsoft 365打开确认没有“文件格式与扩展名不匹配”的警告。如果有警告通常是SAP生成XLS的字节流缺了OLE头信息或者你用的传输方式把二进制内容截断成了文本。这时回查步骤二和三的转换代码重点核对SCMS_XSTRING_TO_BINARY的调用。4.2 如何调试“邮件已提交但外部邮箱收不到”这个问题在实操中碰到的频率最高我按优先级列排查路径新手直接照做第一确认SAP端发件箱状态。事务码SBWP或SBOK查看被发送的对象如果状态是“发送失败”双击看错误消息多数是SMTP服务器拒绝或DNS解析失败。此时检查事务码SCOT的SMTP节点配置确保API、端口、身份验证正确。这里的用户名密码通常是SAP服务账号找BASIS配合确认不要自己乱改。第二确认收件人地址格式。ABAP函数里receiver填写的邮箱地址必须是全小写后缀要完整。很多项目里的用户主数据维护的是SAP用户名而不是邮箱要在SU01中检查用户的“通信”页签是否完整。如果receiver直接填外部邮箱需要确认企业邮件网关是否允许SAP系统发出的匿名邮件。第三有可能SAP系统根本没真正发送只是写入了外发队列。检查事务码SM36定时JOB或者SO_ADMIN里刷新外发队列的程序。有些系统需要手动运行RS$ENPROT或任务“SO_DELIVERY”批量投递。注意如果你没有权限跑这些管理任务找BASIS协调自己在代码里再试一百遍也没用。第四内容太大被邮件服务器拒收。SMTP服务器普遍有附件大小限制比如10MB或25MB。Excel本身不会太大但如果数据量几十万行XLS很容易超过限制。这时候要么分批发送要么压缩成ZIP再挂附件ABAP里有cl_abap_zip压缩二进制数据压缩后效果很明显。5. 常见问题与避坑经验速查我整理了这几年做邮件附件功能时高频出现的问题做成速查表看到哪个症状直接对号入座症状根本原因解决思路附件打开提示格式损坏XLS字节流转换时被截断或编码错误检查SCMS_XSTRING_TO_BINARY是否完整循环全部行邮件收到但附件名是随机数字obj_descr或obj_name没设置对在sodocchgi1里设置obj_descr为业务文件名中文附件名显示乱码编码不支持中文外网环境建议附件名用英文或拼音邮件永远停在“待发送”SMTP配置或外发队列没正常触发检查SCOT配置和SM36定期发送任务附件是0字节内表数据为空或生成函数没返回内容在调用SAP_CONVERT_TO_XLS_FORMAT前确认内表行数功能在测试环境正常生产环境失败生产环境SMTP配置或用户主数据缺失检查SCOT和SU01按生产策略放行Excel带公式列变成静态值XLS格式本身不支持动态公式缓存用ABAP2XLSX生成真正的XLSX或邮件里说明其中“中文附件名乱码”是跨公司协作时最容易翻车的点。我的经验是SAP内部邮件系统支持中文文件名但走外部SMTP网关时很多企业网关只按ISO-8859-1解析邮件头中文会变问号。稳妥做法是附件名统一用英文缩写或拼音正文用中文说明。当然你也可以用BCS接口配合RFC 2047编码附件名这个技术能绕开部分网关限制但引入的复杂度对大多数项目来说不值。还有一个只在新手期容易犯的错误内表数据量巨大时SAP_CONVERT_TO_XLS_FORMAT非常消耗内存。比如一次导50万行销售明细这个函数可能撑爆后台作业的ROLL内存。实操建议是大数据量下必须做数据分块或者走ABAP2XLSX的SAX式流式写入。我在一个项目里遇到40万行库存清单直接调用转换函数导致DIA进程崩溃后来改成每5万行分块、分4个Sheet才解决。做这个功能时一定要提前和业务确认数据量级不能想当然。6. 如何封装成可复用工具如果你们公司类似需求很多我强烈建议你把这个功能封装成通用函数或者类输入内表和目标邮箱输出发送结果。封装的好处不用多说后续任何报表要发邮件直接调用不用每个程序都复制一遍那几十行转换代码。函数设计建议先想好边界条件比如内表为空时要返回“没有数据不发送”的提示数据量超过阈值时自动截断或者提示分批支持多收件人用逗号分隔可选是否附带正文文本文件名字段允许调用方传入。FUNCTION z_util_send_table_as_excel. *------------------------------------------------------------------ **本地接口 * IMPORTING * VALUE(IV_TITLE) TYPE STRING OPTIONAL * VALUE(IV_FILENAME) TYPE STRING * VALUE(IT_DATA) TYPE STANDARD TABLE * VALUE(IV_RECEIVER) TYPE STRING * VALUE(IV_BODY) TYPE STRING OPTIONAL * EXPORTING * VALUE(EV_SUCCESS) TYPE FLAG *------------------------------------------------------------------泛型内表参数it_data在函数里怎么拿到字段结构用动态编程先通过cl_abap_structdescrdescribe_by_data_ref拿到字段列表再把数据逐行写入Excel转换函数要求的i_tab_structure。这个写法有点绕但确实能做到“传任意内表进来都能生成Excel”。实际封装时还要考虑开发者调用方传空值、不支持的字段类型比如内表字段是REF TO类型等异常场景逐步加防御代码。我封装这个函数到第三个版本时才算稳定期间不断收到同事的反馈“能不能顺便加个密码保护”“能不能加上Sheet名配置”——需求永远是改不完的所以接口设计要留足扩展空间。7. 性能优化与后续扩展思路7.1 大数据量导出的几个实用技巧如果数据量超过10万行SAP_CONVERT_TO_XLS_FORMAT会产生一个巨大的内表对象非常吃内存。我在项目中实操下来有几个立竿见影的优化手法使用UNION少、或者直接把SQL结果选择到内表时只挑必输字段别SELECT *。Excel内容少一半生成速度翻倍。设置i_append_mode为空间避免追加模式带来的额外复制开销。如果业务允许导出的数据自动按日期分区比如每天一个Sheet减少单次转换压力。后台执行时用RFC模块加上UPDATE TASK异步发送避免DIA进程阻塞。7.2 更进一步自动生成真正的XLSX如果哪天业务方强化要求必须XLSX比如需要图表透视表或者Excel文件超过旧格式限制直接上ABAP2XLSX。这个开源库在GitHub上有大量案例我有一年做外贸出口项目客户明确要求Excel附件必须带数据有效性下拉框ABAP2XLSX一行方法就能加SAP标准方案完全做不到。引入这个库虽然需要过IT治理审批但长期看带来的灵活性非常值。7.3 定时任务场景的坑后台JOB里跑发送邮件有几个和前台不同的点需要注意。第一发件人信息不能依赖登录用户要专门配置一个SAP服务账号的邮箱作为发件人否则后台JOB的发送结果没法追踪第二JOB日志里不会显示邮件发送失败的具体异常要自己在代码里往应用日志表里写发送结果第三后台JOB所在的应用服务器必须能连通SMTP服务器服务器间的防火墙策略要和网络组提前确认。我见过一个案例测试环境邮件正常上生产后JOB本地执行没问题、但JOB方式就收不到最后查出来是生产应用服务器到邮件网关的网络策略没放行跟ABAP代码一点关系都没有。写在最后的经验做ABAP邮件附件这个功能技术本身难度不高真正的价值在于细心和规范。我见过太多人倒在附件损坏、中文乱码、SMTP队列阻塞这些看似不起眼的坑上。按上面这套方案走先小数据量验证通再封工具、上规模基本不会出大问题。最后分享一个我后来一直沿用的习惯给每次发送操作写应用日志记录发送时间、收件人、附件文件名、数据行数、返回状态码。别嫌麻烦一旦业务方说“我没收到邮件”这些日志就是你排查问题的第一现场省下来的时间足够你多写十个功能了。
返回列表