news 2026/2/22 5:57:14

给ABAP新人的一个小礼物

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
给ABAP新人的一个小礼物

正文伊始,先容鄙人略作自我介绍(AI润色的):

鄙人经营公众号,向来恪守几条默认准则,愿与诸位坦诚相告。

其一,凡拟刊发之文,必先于网络检索一番 —— 若所载内容已有珠玉在前,且思路严谨无误,便不复赘述;偶有疏漏未及检索,致重复分享之况,想来应是寥寥。

其二,文章必以 “干货” 为核,所需代码务求完整详实,绝不以空泛之技术介绍敷衍读者。且既怀诚意分享,便需兼顾兼容性,力求从 R3、ECC 至 S4 系统皆可适配。如此一来,代码或显繁复,语法亦偏古朴,实乃情非得已 —— 断不可只为 S4 用户之便,而忽略其他版本使用者之需。

其三,为保阅读体验纯粹,正文之中绝不穿插广告,免却 “狗皮膏药” 之扰,让诸君能潜心研读。然自今年始,纯以热忱支撑的 “用爱发电” 渐遇瓶颈,故偶有刊发广告文章之况 —— 此类文稿皆由商家拟定,鄙人仅代为发布。其与鄙人原创之文区别甚明:广告文章作者标注为 “咨询共享”,而原创文章为“ABAP老白”,其次原创正文多用 GUI 截图封面,广告封面则风格各异、绝非GUI之截图。幸得诸位兄弟体谅,未曾因偶尔的广告推送便弃我而去,这份厚谊,鄙人铭感五内,唯有以更优质的内容回报厚爱。

偶有友人问及经营公众号之初心,是否意在博名显达?坦诚而言,其间或有三分慕誉之心,然绝非主因 —— 毕竟盛名之滋味,鄙人早于昔年尝过。回溯至 2007 年,鄙人曾于博客间笔耕 ABAP 之学,历时两三载,彼时文章亦算小有热度,承蒙读者厚爱,也算一度薄有虚名。然自省彼时学识尚浅、功力未逮,盛名之下实有难副之虞,遂决然停更,沉潜潜心钻研此道。自 2015 年以降,便以 “国内 ABAP 翘楚” 为自勉之标 —— 世人常言 “文无第一”,然遍观同侪,于 ABAP 技术之深耕细作、正道旁门,鄙人自信暂无出其右者,此语虽有自矜之嫌,亦是肺腑之言。昔年成名之景已然过往,如今重拾笔墨,绝非为再博虚名,所求者,不过是践行心中所愿罢了。

至于 2021 年左右重拾笔墨、开设公众号之缘由,说与诸位或许难以置信,实则为 “行善积德” 四字。双亲年事已高,惟愿其福寿康宁、平安顺遂。古人有云,修桥铺路、济人困厄乃积德之举;今时不同往日,科技昌明,知识普惠亦为大义。鄙人将多年研习之 ABAP 代码、积累之技术心得倾囊分享,于己而言是梳理沉淀,于他人而言或可解燃眉之急、启思维之智,这般以知识为桥、以技术为路的分享,何尝不是一种当代的 “修桥铺路”?愿以微薄之力,助益同行,亦为双亲祈福添寿,此便是公众号存续之核心初心。

介绍完毕,下面是今天的文章正文:

偶有入行不太久的兄弟给我说公众号发的代码看起来比较费劲,希望能有一些适合新手的文章,今天它来了。

熟悉我的人都知道我从来不用Excel做上传或者下载,因为以前都是使用OLE技术来操作Excel,遇到过的坑实在是太多了。当然可能有人用DOI,有人用一些标准函数,但是归根结底都是OLE技术,受限于Excel软件,异常都挺多,所以我一直非常抵触。

后来Excel的新格式XLSX逐渐成为主流,XLSX相对于XLS文件,就是由OpenXML代替了二进制文件格式,然后ZIP打包一下,理论上是可以自己解析或者生成的,不需要再依赖Excel或者WPS,但是因为格式极其复杂,很少有人能够有精力去做这个工作。好在出现了一个开源项目ABAP2XLSX,他已经把大部分的功能都解决了,用起来虽然还是有点复杂,但是已经非常好用,NICE JOB!正常情况下我觉着使用ABAP2XLSX来上传或者下载Excel还是可以的(如果有文档加密的情况,可能还是需要OLE,或者剪贴板的方式),所以今天给大家分享一个使用ABAP2XLSX来上传、下载Excel文件的模板程序。

程序已经把上传和下载代码集成在子程序中,可以直接COPY到其他程序使用,或者放到公共池程序中被其他程序调用。当然使用的前提是已经安装了ABAP2XLSX。

集成的FORM有下面几个特点:

1、上载的子程序参数传入文件全名、从第几行开始导入、导入的列数。上传好的数据则在内表返回

2、下载内表为Excel的子程序,会自动判断是否在ALV界面,如果不是在ALV界面,则只是导出内表内容,如果是在ALV界面的话,会做如下判断和处理:

①如果ALV选择了列,则只导出选择的列

②如果ALV有筛选,则只导出筛选出来的列,也就是ALV看到行数

③导出的Excel文件已经设置自动列宽,并可以设置最大列宽

④数量、金额字段会根据单位、币种自动转换导出数值

⑤日期、时间字段会转为用户设置的格式,如果为INITIAL值,输出空

⑥物料、单位、WBS等有内外数据区别的会转为外部格式

下面演示一下程序的运行,要导入的Excel文件如下

运行程序:

点击“导出Excel”,导出的文件如下:

如果选择了ALV的列,再导出

则导出的Excel为:

如果ALV的字段很长,也可以可以导出的,比如另一个程序,内表里面有个STRING类型的列:

源码如下,希望对初学者写代码有所帮助

*&---------------------------------------------------------------------**& Report ZDEMO_XLSX_UP_DOWN*&---------------------------------------------------------------------**& 演示使用ABAP2XLSX上传/下载Excel文件*&---------------------------------------------------------------------*REPORT zdemo_xlsx_up_down NO STANDARD PAGE HEADING. TABLES:sscrfields.DATA: gt_fldct TYPE lvc_t_fcat WITH HEADER LINE, gs_slayt TYPE lvc_s_layo, gs_varnt LIKE disvariant.DATA: BEGIN OF gt_upld OCCURS 0, rsnum TYPE resb-rsnum, rspos TYPE resb-rspos, END OF gt_upld.DATA: BEGIN OF gt_out OCCURS 0, rsnum TYPE resb-rsnum, rspos TYPE resb-rspos, matnr TYPE resb-matnr, maktx TYPE makt-maktx, werks TYPE resb-werks, bdter TYPE resb-bdter, bdmng TYPE resb-bdmng, meins TYPE resb-meins, enwrt TYPE resb-enwrt, waers TYPE resb-waers, pspel TYPE resb-pspel, END OF gt_out. SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE btxt1.PARAMETERS: p_file TYPE string LOWER CASE MEMORY ID a.SELECTION-SCREEN END OF BLOCK b1. INITIALIZATION. btxt1 = '上传文件'. %_p_file_%_app_%-text = 'Excel文件(*.xlsx)'. PERFORM catset TABLES gt_fldct USING: 'RSNUM' 'RESB' 'RSNUM' '', 'RSPOS' 'RESB' 'RSPOS' '', 'MATNR' 'RESB' 'MATNR' '', 'MAKTX' 'MAKT' 'MAKTX' '', 'WERKS' 'RESB' 'WERKS' '', 'BDTER' 'RESB' 'BDTER' '', 'BDMNG' 'RESB' 'BDMNG' '需求数量'(f01), 'MEINS' 'RESB' 'MEINS' '需求单位'(f02), 'ENWRT' 'RESB' 'ENWRT' '', 'WAERS' 'RESB' 'WAERS' '', 'PSPEL' 'RESB' 'PSPEL' ''. AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file. PERFORM file_f4 CHANGING p_file. AT SELECTION-SCREEN. CASE sscrfields-ucomm . WHEN 'ONLI'. IF p_file IS INITIAL. MESSAGE e000(oo) WITH '请输入完整文件路径'(t01). ENDIF. ENDCASE. START-OF-SELECTION. PERFORM upload_xlsx TABLES gt_upld USING p_file 2 2. PERFORM getdata. PERFORM outdata. *&---------------------------------------------------------------------**& 上载Excel文件,可以指定起始行,指定导入的列数*&---------------------------------------------------------------------*FORM upload_xlsx TABLES t_upload USING pv_file pv_frline pv_cols. DATA: lo_reader TYPE REF TO zif_excel_reader, lo_excel TYPE REF TO zcl_excel, lo_sheet TYPE REF TO zcl_excel_worksheet, ls_cell TYPE zexcel_s_cell_data, lt_cell TYPE zexcel_t_cell_data WITH HEADER LINE. FIELD-SYMBOLS <lv_val> TYPE any. CLEAR t_upload[]. CREATE OBJECT lo_reader TYPE zcl_excel_reader_2007. TRY. lo_excel = lo_reader->load_file( pv_file ). lo_sheet = lo_excel->get_active_worksheet( ). lt_cell[] = lo_sheet->sheet_content. CATCH zcx_excel. MESSAGE e000(oo) WITH '解析Excel数据失败'(t02). ENDTRY. DELETE lt_cell WHERE cell_row < pv_frline. WHILE lt_cell[] IS NOT INITIAL. READ TABLE lt_cell INTO ls_cell INDEX 1. LOOP AT lt_cell WHERE cell_row = ls_cell-cell_row AND cell_column <= pv_cols. ASSIGN COMPONENT lt_cell-cell_column OF STRUCTURE t_upload TO <lv_val>. <lv_val> = lt_cell-cell_value. ENDLOOP. APPEND t_upload. CLEAR t_upload. DELETE lt_cell WHERE cell_row = ls_cell-cell_row. ENDWHILE.ENDFORM. "upload_xlsx *&---------------------------------------------------------------------**& Form getdata*&---------------------------------------------------------------------*FORM getdata. LOOP AT gt_upld. PERFORM alpha_input CHANGING gt_upld-rsnum. MODIFY gt_upld. ENDLOOP. SELECT * INTO CORRESPONDING FIELDS OF TABLE gt_out FROM resb LEFT JOIN makt ON makt~matnr = resb~matnr AND makt~spras = sy-langu FOR ALL ENTRIES IN gt_upld WHERE rsnum = gt_upld-rsnum AND rspos = gt_upld-rspos.ENDFORM. "getdata *&---------------------------------------------------------------------**& Form outdata*&---------------------------------------------------------------------*FORM outdata. gs_slayt-zebra = 'X'. gs_varnt-report = sy-repid. gs_varnt-handle = 1. CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC' EXPORTING it_fieldcat_lvc = gt_fldct[] i_save = 'A' is_variant = gs_varnt is_layout_lvc = gs_slayt i_callback_program = sy-repid i_callback_user_command = 'USER_COMMAND' i_callback_pf_status_set = 'SET_STATUS' TABLES t_outtab = gt_out.ENDFORM. " outdata *&--------------------------------------------------------------------**& Form user_command*&--------------------------------------------------------------------*FORM user_command USING r_ucomm LIKE sy-ucomm r_field TYPE slis_selfield. DATA lv_xlsxraw TYPE xstring. READ TABLE gt_out INDEX r_field-tabindex. CASE r_ucomm. WHEN '&IC1'. "双击 WHEN 'EXPORT'. PERFORM itab2xlsx TABLES gt_out CHANGING lv_xlsxraw. PERFORM downloadxstr USING lv_xlsxraw '' ''. ENDCASE.ENDFORM. "user_command *&---------------------------------------------------------------------**& SET_STATUS*&---------------------------------------------------------------------*FORM set_status USING t_extab TYPE slis_t_extab. SET PF-STATUS 'STD_FULL' EXCLUDING t_extab.ENDFORM. "SET_PF_STATUS *&---------------------------------------------------------------------**& Form catset*&---------------------------------------------------------------------*FORM catset TABLES fldcat USING pv_field pv_reftab pv_reffld pv_text. DATA: ls_fldcat TYPE lvc_s_fcat. ls_fldcat-fieldname = pv_field. ls_fldcat-reptext = pv_text. ls_fldcat-coltext = pv_text. ls_fldcat-colddictxt = 'R'. ls_fldcat-selddictxt = 'R'. ls_fldcat-ref_table = pv_reftab. ls_fldcat-ref_field = pv_reffld. ls_fldcat-col_opt = 'A'. CASE ls_fldcat-fieldname. WHEN 'BDMNG'. ls_fldcat-qfieldname = 'MEINS'. ls_fldcat-no_zero = 'X'. WHEN 'ENWRT'. ls_fldcat-cfieldname = 'WAERS'. ls_fldcat-no_zero = 'X'. ENDCASE. APPEND ls_fldcat TO fldcat . CLEAR ls_fldcat .ENDFORM. "catset *&---------------------------------------------------------------------**& ALV界面/单纯内表 转为Excel文件*&---------------------------------------------------------------------*FORM itab2xlsx TABLES t_intab CHANGING cv_xlsxraw. DATA: lo_excel TYPE REF TO zcl_excel, lo_sheet TYPE REF TO zcl_excel_worksheet, lo_writer TYPE REF TO zif_excel_writer, lo_style_top TYPE REF TO zcl_excel_style, lo_column TYPE REF TO zcl_excel_column, lv_style_top TYPE zexcel_cell_style. DATA: lr_grid TYPE REF TO cl_gui_alv_grid, lt_fcat TYPE lvc_t_fcat WITH HEADER LINE, lt_cols TYPE lvc_t_col WITH HEADER LINE, lt_filt TYPE lvc_t_fidx WITH HEADER LINE. DATA: lr_tdescr TYPE REF TO cl_abap_tabledescr, lr_sdescr TYPE REF TO cl_abap_structdescr, ls_comps LIKE LINE OF cl_abap_structdescr=>components. DATA: lv_fmname TYPE funcname. DATA: lv_tmpstr TYPE string. DATA: lv_rownum TYPE i. DATA: lv_colnum TYPE i. DATA: lv_width TYPE i. DATA: lv_type TYPE c. DATA: lv_char TYPE char256. DATA: BEGIN OF lt_width OCCURS 0, column TYPE i, width TYPE i, END OF lt_width. FIELD-SYMBOLS <lv_fldval> TYPE any. FIELD-SYMBOLS <lv_curr> TYPE any. CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR' IMPORTING e_grid = lr_grid. IF lr_grid IS BOUND. lr_grid->get_frontend_fieldcatalog( IMPORTING et_fieldcatalog = lt_fcat[] ). lr_grid->get_selected_columns( IMPORTING et_index_columns = lt_cols[] ). lr_grid->get_filtered_entries( IMPORTING et_filtered_entries = lt_filt[] ). DELETE lt_fcat WHERE no_out = 'X' OR tech = 'X'. IF lt_cols[] IS NOT INITIAL. LOOP AT lt_fcat. READ TABLE lt_cols WITH KEY fieldname = lt_fcat-fieldname. IF sy-subrc NE 0. DELETE lt_fcat. CONTINUE. ENDIF. IF lt_fcat-coltext IS INITIAL. lt_fcat-coltext = lt_fcat-seltext. ENDIF. MODIFY lt_fcat. ENDLOOP. ENDIF. ELSE. lr_tdescr ?= cl_abap_typedescr=>describe_by_data( t_intab[] ). lr_sdescr ?= lr_tdescr->get_table_line_type( ). LOOP AT lr_sdescr->components INTO ls_comps. CHECK ls_comps-type_kind NA 'h'. lt_fcat-fieldname = ls_comps-name. lt_fcat-coltext = ls_comps-name. APPEND lt_fcat. ENDLOOP. ENDIF. TRY. CREATE OBJECT lo_excel. CREATE OBJECT lo_writer TYPE zcl_excel_writer_2007. lo_sheet = lo_excel->get_active_worksheet( ). lo_style_top = lo_excel->add_new_style( ). lo_style_top->alignment->vertical = zcl_excel_style_alignment=>c_vertical_top. lo_style_top->alignment->wraptext = abap_true. lv_style_top = lo_style_top->get_guid( ). LOOP AT lt_fcat. lt_width-column = sy-tabix. lt_width-width = cl_abap_list_utilities=>dynamic_output_length( lt_fcat-coltext ) + 1. APPEND lt_width. lo_sheet->set_cell( ip_column = sy-tabix ip_row = 1 ip_value = lt_fcat-coltext ip_style = lv_style_top ). ENDLOOP. LOOP AT t_intab. lv_rownum = sy-tabix + 1. lv_colnum = 0. READ TABLE lt_filt WITH TABLE KEY table_line = sy-tabix. CHECK sy-subrc NE 0. LOOP AT lt_fcat. ASSIGN COMPONENT lt_fcat-fieldname OF STRUCTURE t_intab TO <lv_fldval>. CHECK sy-subrc = 0. DESCRIBE FIELD <lv_fldval> TYPE lv_type. CASE lv_type. WHEN 'I' OR 'P' OR 'F' OR 'a' OR 'e' OR 'b' OR 's'. IF lt_fcat-cfieldname IS NOT INITIAL. ASSIGN COMPONENT lt_fcat-cfieldname OF STRUCTURE t_intab TO <lv_curr>. IF sy-subrc = 0. WRITE <lv_fldval> TO lv_char CURRENCY <lv_curr>. ELSE. lv_char = abs( <lv_fldval> ). IF <lv_fldval> < 0. CONCATENATE '-' lv_char INTO lv_char. ENDIF. ENDIF. ELSE. lv_char = abs( <lv_fldval> ). IF <lv_fldval> < 0. CONCATENATE '-' lv_char INTO lv_char. ENDIF. ENDIF. CONDENSE lv_char NO-GAPS. lv_tmpstr = lv_char. WHEN 'D' OR 'T'. IF <lv_fldval> IS INITIAL OR <lv_fldval> = ''. lv_char = ''. ELSE. WRITE <lv_fldval> TO lv_char. ENDIF. lv_tmpstr = lv_char. WHEN 'X' OR 'y' OR 'g'. lv_tmpstr = <lv_fldval>. WHEN OTHERS. WRITE <lv_fldval> TO lv_char. lv_tmpstr = lv_char. ENDCASE. IF lv_type = 'N' AND lt_fcat-edit_mask IS INITIAL. PACK lv_tmpstr TO lv_tmpstr. ENDIF. lv_colnum = lv_colnum + 1. lo_sheet->set_cell( ip_column = lv_colnum ip_row = lv_rownum ip_value = lv_tmpstr ip_style = lv_style_top ). READ TABLE lt_width WITH KEY column = lv_colnum BINARY SEARCH. lv_width = cl_abap_list_utilities=>dynamic_output_length( lv_tmpstr ) + 1. IF lt_width-width < lv_width. lt_width-width = lv_width. MODIFY lt_width INDEX sy-tabix. ENDIF. ENDLOOP. ENDLOOP. LOOP AT lt_width. IF lt_width-width > 40. lt_width-width = 40. ENDIF. lo_column = lo_sheet->get_column( ip_column = lt_width-column ). lo_column->set_width( ip_width = lt_width-width ). ENDLOOP. cv_xlsxraw = lo_writer->write_file( lo_excel ). CATCH cx_root. MESSAGE e000(oo) WITH 'ABAP2XLSX出错'. ENDTRY.ENDFORM. "itab2xlsx *&---------------------------------------------------------------------**& 下载XSTRING到本地*&---------------------------------------------------------------------*FORM downloadxstr USING pv_xstr pv_file pv_default. DATA lt_btab TYPE TABLE OF w3mime. DATA lv_path TYPE string. DATA lv_file TYPE string. DATA lv_blen TYPE i. CALL FUNCTION 'SCMS_XSTRING_TO_BINARY' EXPORTING buffer = pv_xstr IMPORTING output_length = lv_blen TABLES binary_tab = lt_btab. IF pv_file IS INITIAL. cl_gui_frontend_services=>file_save_dialog( EXPORTING default_extension = 'XLSX' default_file_name = pv_default file_filter = 'Excel文件(*.XLSX)|*.XLSX' CHANGING filename = lv_path path = lv_path fullpath = lv_file EXCEPTIONS OTHERS = 4 ). ELSE. lv_file = pv_file. ENDIF. CHECK lv_file IS NOT INITIAL. CALL FUNCTION 'GUI_DOWNLOAD' EXPORTING bin_filesize = lv_blen filename = lv_file filetype = 'BIN' confirm_overwrite = '' TABLES data_tab = lt_btab EXCEPTIONS OTHERS = 22. IF sy-subrc NE 0. MESSAGE ID sy-msgid TYPE 'E' NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. ENDIF.ENDFORM. "downxstr *&---------------------------------------------------------------------**& Form ALPHA_INPUT*&---------------------------------------------------------------------*FORM alpha_input CHANGING field. CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT' EXPORTING input = field IMPORTING output = field.ENDFORM. "ALPHA_INPUT *&---------------------------------------------------------------------**& file_f4*&---------------------------------------------------------------------*FORM file_f4 CHANGING cv_file . DATA lt_file TYPE filetable WITH HEADER LINE. DATA lv_code TYPE i. CALL METHOD cl_gui_frontend_services=>file_open_dialog EXPORTING file_filter = 'Excel File(*.xlsx)|*.xlsx|所有文件(*.*)|*.*' multiselection = '' CHANGING file_table = lt_file[] rc = lv_code EXCEPTIONS OTHERS = 5. CHECK lv_code = 1. IF sy-subrc NE 0. MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. ELSE. READ TABLE lt_file INDEX 1. cv_file = lt_file. ENDIF.ENDFORM. "file_f4

公众号有ABAP交流群,可以加我微信拉你入群。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!