场景:ALV显示数据在100W以上,用户需要下载EXCEL下来,但通过EXCEL是下载不下来的,采用CSV格式下载勉强能够到本地,但由于CSV特别的符号,比如逗号、双引号造成CSV格式混乱,需要加按钮下载。
方案:通过下载CSV格式。并将特殊符号替换,通过服务器下载到服务器上某个位置,再通过函数ARCHIVFILE_SERVER_TO_CLIENT直接下载到本地
建议点:一、CSV格式,可以扩展到100万以上的数据
二、如果本机内存不够,建议进行分文件下载。
我采用的方案二。
单个文件代码如下 :
METHOD down_excel_to_services.
FIELD-SYMBOLS: <itab> TYPE STANDARD TABLE,
<wa> TYPE any,
<fs_value> TYPE any.
DATA: dy_line TYPE REF TO data.
DATA: lv_line TYPE string. " 单行数据
DATA: lv_str TYPE string.
DATA: lv_path_name TYPE string.
DATA: ls_fieldcat TYPE lvc_s_fcat.
DATA: ls_char TYPE abap_cr_lf,
lv_hc TYPE c,
lv_hh TYPE c.
DATA: lv_seq TYPE string.
CONSTANTS: lc_dquote TYPE string VALUE '""',
lc_quote TYPE char1 VALUE '"',
lc_comma TYPE char1 VALUE ','.
" 换行符常量
DATA: lv_crlf TYPE abap_cr_lf VALUE cl_abap_char_utilities=>cr_lf.
DATA: lv_cr TYPE c LENGTH 1,
lv_lf TYPE c LENGTH 1.
" 通过截取获得 CR (回车) 和 LF (换行)
lv_cr = lv_crlf+0(1). " 0D
lv_lf = lv_crlf+1(1). " 0A
" 1. 处理序号,确保文件名唯一
IF iv_index IS NOT INITIAL.
lv_seq = |_{ iv_index }|. " 例如生成 _1, _2
ELSE.
lv_seq = ''.
ENDIF.
" 1. 动态创建内表结构 (假设你已经有 it_fieldcat)
CALL METHOD cl_alv_table_create=>create_dynamic_table
EXPORTING
it_fieldcatalog = it_fieldcat
IMPORTING
ep_table = dy_table.
ASSIGN dy_table->* TO <itab>.
CREATE DATA dy_line LIKE LINE OF <itab>.
ASSIGN dy_line->* TO <wa>.
" 2. 获取数据
" 注意:直接引用传入的表,避免内存复制
ASSIGN it_tab TO <itab>.
IF <itab> IS INITIAL.
RETURN.
ENDIF.
" 3. 生成文件名
CLEAR:mv_file.
lv_path_name = sy-uname && '-' && sy-datum && '-' && sy-uzeit && lv_seq && '.csv'.
CLEAR mv_file.
CONCATENATE mc_file lv_path_name INTO mv_file.
" 4. 打开文件 (关键优化:使用 TEXT MODE,UTF-8 编码)
" WITH BYTE-ORDER MARK 会在文件头自动加上 BOM,Excel打开不会乱码
OPEN DATASET mv_file FOR OUTPUT IN TEXT MODE ENCODING UTF-8 WITH BYTE-ORDER MARK.
IF sy-subrc <> 0.
mc_type = 'E'.
mc_msg = '无法打开服务器文件'.
RETURN.
ENDIF.
" 5. 写入表头 (可选)
IF mv_line1 IS NOT INITIAL.
TRANSFER mv_line1 TO mv_file.
ENDIF.
" 6. 循环处理数据并逐行写入
LOOP AT <itab> ASSIGNING <wa>.
CLEAR lv_line.
LOOP AT it_fieldcat INTO ls_fieldcat.
ASSIGN COMPONENT ls_fieldcat-fieldname OF STRUCTURE <wa> TO <fs_value>.
IF <fs_value> IS ASSIGNED.
lv_str = <fs_value>.
CONDENSE lv_str NO-GAPS.
" --- A. 数值类型高性能处理 (手动移负号) ---
" P=Packed, I=Integer, F=Float, X=Byte(很少用但归类在此)
IF ls_fieldcat-inttype CA 'PIFX'.
" 只有小于 0 的时候才需要处理
IF <fs_value> < 0.
lv_str = <fs_value> * -1.
CONCATENATE '-' lv_str INTO lv_str.
ENDIF.
ENDIF.
" 清理回车换行,防止破坏 CSV 格式
IF lv_str CA lv_crlf.
REPLACE ALL OCCURRENCES OF lv_crlf IN lv_str WITH ' '.
REPLACE ALL OCCURRENCES OF lv_cr IN lv_str WITH ' '.
REPLACE ALL OCCURRENCES OF lv_lf IN lv_str WITH ' '.
ENDIF.
" 检查是否存在 CSV 的敏感字符:逗号 或 双引号
IF lv_str CS lc_comma OR lv_str CS lc_quote.
" 1. 内部双引号转义: " -> ""
REPLACE ALL OCCURRENCES OF lc_quote IN lv_str WITH lc_dquote.
CONCATENATE lc_quote lv_str lc_quote INTO lv_str.
ENDIF.
" 拼接CSV格式:如果字段内容包含逗号,建议用双引号包起来,例如: "Content, with comma"
" 这里沿用你的简单逻辑:值 + 逗号
CONCATENATE lv_line lv_str ',' INTO lv_line.
ELSE.
CONCATENATE lv_line lc_comma INTO lv_line.
ENDIF.
ENDLOOP.
" TEXT MODE 下 TRANSFER 会自动在末尾添加换行符,所以不需要手动加 cr_lf
TRANSFER lv_line TO mv_file.
ENDLOOP.
" 7. 关闭文件
CLOSE DATASET mv_file.
mc_type = 'S'.
mt_path = VALUE #( BASE mt_path ( lv_path_name ) ).
" 释放内存
UNASSIGN <itab>.
UNASSIGN <wa>.
ENDMETHOD.
通过游标取数,按分包下载:
DATA:lt_out LIKE TABLE OF gt_out,
lt_down_excel LIKE TABLE OF gt_out.
DATA: lv_cursor TYPE cursor,
lv_val TYPE zterpconf-zval1,
lv_max TYPE i.
DATA: lv_loop_index TYPE i.
FIELD-SYMBOLS : <fs_excel> TYPE ANY TABLE. "取出的表数据
DATA : l_refitab TYPE REF TO data.
DATA:lcl TYPE REF TO zcl_process_ayn.
CREATE OBJECT lcl.
* 校验
IF gt_fieldcat IS INITIAL.
MESSAGE '无字段结构,不允许操作' TYPE 'S' DISPLAY LIKE 'E' .
ENDIF .
CHECK gt_fieldcat IS NOT INITIAL .
IF gt_out IS INITIAL.
MESSAGE '未找到数据' TYPE 'S' DISPLAY LIKE 'E' .
ENDIF.
CHECK gt_out IS NOT INITIAL .
""取表头描述
SELECT fieldname, ref_table,ref_field,cfieldname, coltext FROM @gt_fieldcat AS t INTO TABLE @DATA(lt_title) .
CLEAR:lcl->mv_line1,lcl->mc_type,lcl->mt_path.
LOOP AT lt_title INTO DATA(ls_title).
"构造导出至服务器数据表头
CONCATENATE lcl->mv_line1 ls_title-coltext ',' INTO lcl->mv_line1 . "每个数据后面加个逗号,excel就会换单元格
ENDLOOP.
lcl->mc_numb = lines( gt_out ).
CREATE DATA l_refitab TYPE TABLE OF ty_out.
ASSIGN l_refitab->* TO <fs_excel>.
OPEN CURSOR WITH HOLD @lv_cursor FOR
SELECT * FROM @gt_out AS t BYPASSING BUFFER.
SELECT SINGLE zval1 INTO lv_val FROM zterpconf WHERE zkey1 = 'ZCO009' AND zkey2 = 'EXPORT' AND zkey3 = 'PACKAGE' .
lv_max = lv_val.
IF lv_max IS INITIAL.
lv_max = lcl->mc_numb.
ENDIF.
DO .
lv_loop_index = sy-index. "保存当前是第几次循环
FETCH NEXT CURSOR @LV_cursor INTO CORRESPONDING FIELDS OF TABLE @<fs_excel> PACKAGE SIZE @lv_max.
IF sy-subrc <> 0.
EXIT. " 数据读完了,退出大循环
ENDIF.
CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
EXPORTING
text = |正在处理第 { lv_loop_index } 批数据...|.
lcl->down_excel_to_services( EXPORTING it_fieldcat = gt_fieldcat it_tab = <fs_excel> iv_index = lv_loop_index ).
ENDDO.
UNASSIGN <fs_excel>.
* 关闭连接
lcl->close( CHANGING iv_cursor = lv_cursor ).
* 下载文件至客户端
IF lcl->mc_type EQ 'S'.
DATA:lv_serv_path LIKE sapb-sappfad,
lv_local_path LIKE sapb-sappfad.
LOOP AT lcl->mt_path INTO DATA(lw_file_name).
CONCATENATE lcl->mc_file lw_file_name INTO lv_serv_path.
CONCATENATE lcl->mc_file_path lw_file_name INTO lv_local_path.
CALL FUNCTION 'ARCHIVFILE_SERVER_TO_CLIENT'
EXPORTING
path = lv_serv_path " 服务器源路径
targetpath = lv_local_path " 本地目标路径
EXCEPTIONS
error_file = 1
OTHERS = 2.
IF sy-subrc = 0.
MESSAGE '下载成功,文件地址:' && lv_local_path TYPE 'S'.
" 可选:下载完成后删除服务器临时文件
ELSE.
MESSAGE '下载失败' TYPE 'E'.
ENDIF.
ENDLOOP.
删除文件
lcl->get_files( lcl->mc_file ).
总结思路:1、通过游标分包读取
2、每包数据生成CSV文件到服务器AL11可查询
3、从服务器上下载文件到本地,函数:ARCHIVFILE_SERVER_TO_CLIENT
4、删除服务器上文件。
100W以上数据,很好地解决了用户的问题
991

被折叠的 条评论
为什么被折叠?



