大数据量下载ALV,解决方案

场景: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以上数据,很好地解决了用户的问题

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值