[ABAP]Excelファイルのダウンロード・アップロードについて調べてみた（Upload/Download Excel File in ABAP）

こんにちは。

今回はABAPでのExcelファイルのダウンロード・アップロードについてまとめました。

当初は、ググって拾ったネタを紹介する程度に考えていたのですが、どうせならと
動作確認をしてみたところかなり奥深い内容でした(笑)

それでは見ていきましょう。

###仕様環境

Windows10、Excel2013、BASIS753、ADT(Eclipse2019-12)
他環境だと多少の挙動の差があるかと思います。事前にご承知おきください。

###更新履歴
2021/01/17 1.5. 追加

##1. Excelダウンロード

###1.1. タブ区切りテキストファイルとしてダウンロード

内部テーブルを、GUI_DOWNLOADなどで「タブ区切り」のASC形式でダウンロードする方法です。
SAP Enteprise（4.7）あたりまではこれが鉄板ネタでした。
※ALVなどのSAP標準機能でも、ダウンロード形式の「スプレッドシート」を選ぶと
Excelファイルではなく、タブ区切りテキストファイルをダウンロードしていたような時代でした。

####拡張子は「xls」限定

拡張子を現在のExcelワークシートの拡張子である「xlsx」とした場合、ファイルを開くことができなくなります。
そのため拡張子は「xls」とする必要があります。
「xls」の場合でも、Excelの仕様でファイルを開く前にファイルの安全性を確認するポップアップ画面が表示されます。

#####拡張子「xlsx」のファイルを開いた時のポップアップ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/7de1b423-6c71-12a5-6874-d18e2754adbd.png)

#####拡張子「xls」のファイルをを開いた時のポップアップ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/39a74511-1b2a-8f97-7da6-a4f5b9fc6432.png)

また、拡張子が「xls」の場合は、Excelは65535行までしかデータを表示することができません。
よって、対象データの行数が超過する場合は処理件数超過エラーを出すなどの仕様が必要となります。
[Excelの最大列数/最大行数は？]
(http://propg.ee-mall.info/excel/excel%E3%81%AE%E6%9C%80%E5%A4%A7%E5%88%97%E6%95%B0%E6%9C%80%E5%A4%A7%E8%A1%8C%E6%95%B0%E3%81%AF%EF%BC%9F/)

####Excelにダウンロード値を変更されないためのアドオン仕様検討

#####現象
・数字のみのコード値の前ゼロを除去する
・数字の間に「E」が挟まったコード値を指数表記と認識する（例：「10E5」→「1.00E+06」）

#####対策
値の先頭に’(アポストロフィ)を付与して文字列扱いとする
値を引用句（ダブルクォーテーション）で囲う
など


###1.2. 汎用モジュール「SAP_CONVERT_TO_XLS_FORMAT」を使用する

昔はこれでExcelファイルの対応ができたようなのですが、今はダウンロードされたファイルが開けませんでした。
ググったところ、SAP COMMUNITYで「Excel2003で動いていたのにExcel2007で動かなくなった」という
質問が見つかりました。
[problem in SAP_CONVERT_TO_XLS_FORMAT]
(https://answers.sap.com/questions/3707705/problem-in-sapconverttoxlsformat.html)

####サンプルコード

<details><summary>SAP_CONVERT_TO_XLS_FORMAT使用</summary><div>

```abap:SAMPLE_SAP_CONVERT_TO_XLS_FORMAT
REPORT y_test_gohta12.

* NOT working correctly. Downloaded Excel file cannot be opened.

DATA: w_path  TYPE string,
      w_fname TYPE string.

DATA: itab_mara TYPE STANDARD TABLE OF mara.
PARAMETERS: p_dl TYPE string.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_dl.

  cl_gui_frontend_services=>file_save_dialog(
     CHANGING
       filename = w_fname
       path = w_path
       fullpath = p_dl                ).

START-OF-SELECTION.

  SELECT FROM mara
    INNER JOIN makt
    ON mara~matnr = makt~matnr
    INNER JOIN marc
    ON  mara~matnr = marc~matnr
    INNER JOIN mard
    ON   marc~matnr = mard~matnr
    AND marc~werks = mard~werks
    FIELDS mara~matnr,
               marc~werks,
               marc~dispr,
               marc~dismm,
               mard~lgort,
               mard~labst
     INTO TABLE @DATA(it_ct_table).

  CALL FUNCTION 'SAP_CONVERT_TO_XLS_FORMAT'
    EXPORTING
      i_filename        = CONV char128( p_dl )
      i_field_seperator = '#'   "cl_abap_char_utilities=>horizontal_tab
*     i_line_header     = abap_on
*     i_appl_keep       = abap_on
    TABLES
      i_tab_sap_data    = it_ct_table.
```
</div></detail>

###1.3. XMLファイルを作成しダウンロード

Excelファイル(拡張子.xlsx)形式でのダウンロードを実現するソリューションとしては、
これが一番わかりやすいです。

####参照した記事
[Excel file (*.xlsx) Export/Import]
(https://blogs.sap.com/2019/06/06/excel-file-.xlsx-exportimport/)

※上記のサイトのソリューションにて、
`Include –  TEST_GHO_DATA_UPLD_DWLD_FILF01`
`Form – EXTRACT_TO_EXCEL`
という記載がありますが、現在のIncludeは「GHO_DATA_UPLD_DWLD_FILF01」になっているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/debbb0f1-639b-d1d7-29d1-73afca8b89e9.png)

#####ロジックの概要
・内部テーブルからALVフィールドカタログ情報を取得
・内部テーブルとフィールドカタログ情報からxlsx形式またはmhtml形式のXMLファイルを作成
・XMLデータのバイナリ変換を行い、バイナリ形式でダウンロード

#####サンプルコード
INCLUDE内部で使用している「P_DWPATH」のところでエラーになって解消しなかったので、
INCLUDEのソースを取り込んで作成しました（日本語のコメントは当方記入）。

<details><summary>サンプルコード(内部テーブル→XML→バイナリ)</summary><div>

```abap:SAMPLE02
*&---------------------------------------------------------------------*
*& Report y_test_gohta13
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT y_test_gohta13.

*INCLUDE GHO_DATA_UPLD_DWLD_FILF01.

DATA:
      w_path     TYPE string,
      w_fname    TYPE string,
      it_sort    TYPE lvc_t_sort,
      it_filt    TYPE lvc_t_filt,
      is_layout  TYPE lvc_s_layo,
      ie_xstring TYPE xstring.

PARAMETERS p_dwpath TYPE string.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_dwpath.

* 保存先指定
  cl_gui_frontend_services=>file_save_dialog(
     CHANGING
       filename = w_fname
       path = w_path
       fullpath = p_dwpath                ).

START-OF-SELECTION.

  SELECT FROM mara
    INNER JOIN makt
    ON mara~matnr = makt~matnr
    INNER JOIN marc
    ON  mara~matnr = marc~matnr
    INNER JOIN mard
    ON   marc~matnr = mard~matnr
    AND marc~werks = mard~werks
    FIELDS mara~matnr,
               marc~werks,
               marc~dispr,
               marc~dismm,
               mard~lgort,
               mard~labst
     INTO TABLE @DATA(it_ct_table).

  PERFORM  extract_to_excel
     USING 'XXXXXXXXXX' "汎用M LVC_FIELDCATALOG_MERGEで使うためのテーブル名
           it_sort
           it_filt
           is_layout
           abap_on
      CHANGING
            it_ct_table
            ie_xstring.

* BELOW from HERE is copied from
*INCLUDE  GHO_DATA_UPLD_DWLD_FILF01
* FORM extract_to_excel

FORM extract_to_excel
      USING
          iv_structure_name TYPE dd02l-tabname
          it_sort TYPE lvc_t_sort
          it_filt TYPE lvc_t_filt
          is_layout TYPE lvc_s_layo
          i_xlsx TYPE flag
      CHANGING
          ct_data TYPE STANDARD TABLE
          e_xstring TYPE xstring.

  DATA:
    lv_size   TYPE i,
    lt_bintab TYPE solix_tab.

  DATA: lt_fieldcat TYPE lvc_t_fcat.
  DATA: mt_fcat TYPE lvc_t_fcat.
  DATA: mt_data TYPE REF TO data.
  DATA: m_flavour TYPE string.
  DATA: m_version TYPE string.
  DATA: mo_result_data TYPE REF TO cl_salv_ex_result_data_table.
  DATA: mo_columns  TYPE REF TO cl_salv_columns_table.
  DATA: mo_aggreg   TYPE REF TO cl_salv_aggregations.
  DATA: mo_salv_table  TYPE REF TO cl_salv_table.
  DATA: m_file_type TYPE salv_bs_constant.
  FIELD-SYMBOLS <tab> TYPE ANY TABLE.

  GET REFERENCE OF ct_data INTO mt_data.

  " FIELDCATALOG
  CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
    EXPORTING
*     I_BUFFER_ACTIVE        =
      i_structure_name       = iv_structure_name
*     I_CLIENT_NEVER_DISPLAY = 'X'
*     I_BYPASSING_BUFFER     =
*     I_INTERNAL_TABNAME     =
    CHANGING
      ct_fieldcat            = lt_fieldcat
    EXCEPTIONS
      inconsistent_interface = 1
      program_error          = 2
      OTHERS                 = 3.
  IF sy-subrc <> 0.
* Implement suitable error handling here
  ENDIF.

* 行見出しの設定
  PERFORM get_text
              CHANGING
                 lt_fieldcat.

*if we didn't pass fieldcatalog we need to create it
* フィールドカタログが使用できない場合は以下で作成
  IF lt_fieldcat[] IS INITIAL.
* 内部テーブルのデータからALV用インスタンスを生成
    ASSIGN mt_data->* TO <tab>.
    TRY .
        cl_salv_table=>factory(
        EXPORTING
          list_display = abap_false
        IMPORTING
          r_salv_table = mo_salv_table
        CHANGING
          t_table      = <tab> ).
      CATCH cx_salv_msg.
    ENDTRY.

    "get colums & aggregation info to create fieldcat
* フィールドカタログ作成用にコラム情報と集計情報を取得
    mo_columns  = mo_salv_table->get_columns( ).
    mo_aggreg   = mo_salv_table->get_aggregations( ).
    mt_fcat     =  cl_salv_controller_metadata=>get_lvc_fieldcatalog(
                                  r_columns      = mo_columns
                                  r_aggregations = mo_aggreg ).
  ELSE.
*else we take the one we passed
    mt_fcat[] = lt_fieldcat[].
  ENDIF.

  IF cl_salv_bs_a_xml_base=>get_version( ) EQ if_salv_bs_xml=>version_25 OR
     cl_salv_bs_a_xml_base=>get_version( ) EQ if_salv_bs_xml=>version_26.

    mo_result_data = cl_salv_ex_util=>factory_result_data_table(
        r_data                      = mt_data
        s_layout                    = is_layout
        t_fieldcatalog              = mt_fcat
        t_sort                      = it_sort
        t_filter                    = it_filt
    ).

* XMLバージョン確認
    CASE cl_salv_bs_a_xml_base=>get_version( ).
      WHEN if_salv_bs_xml=>version_25.
        m_version = if_salv_bs_xml=>version_25.
      WHEN if_salv_bs_xml=>version_26.
        m_version = if_salv_bs_xml=>version_26.
    ENDCASE.

    "if we flag i_XLSX then we'll create XLSX if not then MHTML excel file
* ファイルタイプ設定（XLSXかMHTML）
    IF i_xlsx IS NOT INITIAL.
      m_file_type = if_salv_bs_xml=>c_type_xlsx.
    ELSE.
      m_file_type = if_salv_bs_xml=>c_type_mhtml.
    ENDIF.

    m_flavour = if_salv_bs_c_tt=>c_tt_xml_flavour_export.
    "transformation of data to excel
*  指定されたバージョンでのXMLファイルへの変換（XSTRING形式）
    CALL METHOD cl_salv_bs_tt_util=>if_salv_bs_tt_util~transform
      EXPORTING
        xml_type      = m_file_type
        xml_version   = m_version
        r_result_data = mo_result_data
        xml_flavour   = m_flavour
        gui_type      = if_salv_bs_xml=>c_type_xlsx
      IMPORTING
        xml           = e_xstring.
  ENDIF.

* XMLファイル（XSTRING形式）のバイナリ化
  IF e_xstring IS NOT INITIAL.
    "save file
    CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
      EXPORTING
        buffer        = e_xstring
*       APPEND_TO_TABLE = ' '
      IMPORTING
        output_length = lv_size
      TABLES
        binary_tab    = lt_bintab.

* バイナリファイルのダウンロード
    cl_gui_frontend_services=>gui_download(
      EXPORTING
        bin_filesize              = lv_size
        filename                  = p_dwpath
        filetype                  = 'BIN'
      CHANGING
        data_tab                  = lt_bintab
      EXCEPTIONS
        file_write_error          = 1
        no_batch                  = 2
        gui_refuse_filetransfer   = 3
        invalid_type              = 4
        no_authority              = 5
        unknown_error             = 6
        header_not_allowed        = 7
        separator_not_allowed     = 8
        filesize_not_allowed      = 9
        header_too_long           = 10
        dp_error_create           = 11
        dp_error_send             = 12
        dp_error_write            = 13
        unknown_dp_error          = 14
        access_denied             = 15
        dp_out_of_memory          = 16
        disk_full                 = 17
        dp_timeout                = 18
        file_not_found            = 19
        dataprovider_exception    = 20
        control_flush_error       = 21
        not_supported_by_gui      = 22
        error_no_gui              = 23
        OTHERS                    = 24
           ).
    IF sy-subrc <> 0.
*     Implement suitable error handling here
    ENDIF.

  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  GET_TEXT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      <--ct_fieldcat  text
*----------------------------------------------------------------------*
FORM get_text  CHANGING ct_fieldcat TYPE lvc_t_fcat.

* fieldnameとreptextは使用するテーブルにより書き換える

  FIELD-SYMBOLS: <fs_fieldcat> TYPE lvc_s_fcat.

  LOOP AT ct_fieldcat ASSIGNING <fs_fieldcat>.
    CASE <fs_fieldcat>-fieldname.
      WHEN 'NET_ID' OR 'GHO_NET_ID'.
        <fs_fieldcat>-reptext = 'Production Network'(013).
      WHEN 'ALLOC_BASIS'.
        <fs_fieldcat>-reptext = 'Allocation Basis'(014).
      WHEN 'NUM_OF_TESTS'.
        <fs_fieldcat>-reptext = 'No. of Tests'(015).
      WHEN 'QTY_TYPE' OR 'QUANTITY_TYPE'.
        <fs_fieldcat>-reptext = 'Quantity Type'(016).
      WHEN 'DEP_QTY_TYPE'.
        <fs_fieldcat>-reptext = 'Dependent Material Quantity Type'(017).
      WHEN 'CMPNT_ALLOCATION'.
        <fs_fieldcat>-reptext = 'Component'(018).
      WHEN 'MASTER_INDICATOR'.
        <fs_fieldcat>-reptext = 'Master UOM'(019).
      WHEN 'DECIMAL_RANGE'.
        <fs_fieldcat>-reptext = 'Decimals in result'(020).
      WHEN 'DEPENDED_DISP'.
        <fs_fieldcat>-reptext = 'Dependent Quantity Type'(021).
      WHEN 'NETOBJ'.
        <fs_fieldcat>-reptext = 'Production Network'(022).
      WHEN 'PROD_DATE'.
        <fs_fieldcat>-reptext = 'Production Date(DD.MM.YYYY)'(023).
      WHEN 'PROD_TIME'.
        <fs_fieldcat>-reptext = 'Production Time(HH:MM:SS)'(024).
      WHEN 'UOM'.
        <fs_fieldcat>-reptext = 'UoM'(025).
      WHEN 'MDC_VALUE'.
        <fs_fieldcat>-reptext = 'Quantity'(026).
      WHEN 'MDC_PARAMETER' OR 'CHARACTERISTICS'.
        <fs_fieldcat>-reptext = 'Observed Reading'.
      WHEN 'GHO_NODE_ID'.
        <fs_fieldcat>-reptext = 'Network Object'.
      WHEN 'GHO_WC_ID'.
        <fs_fieldcat>-reptext = 'Well Completion'.
      WHEN 'GHO_WELL_ID'.
        <fs_fieldcat>-reptext = 'Well'.
      WHEN 'GHO_START_OF_TEST_DATE'.
        <fs_fieldcat>-reptext = 'Start of Test Date(DD.MM.YYYY)'.
      WHEN 'GHO_START_OF_TEST_TIME'.
        <fs_fieldcat>-reptext = 'Start of Test Time(HH:MM:SS)'.
      WHEN 'GHO_END_OF_TEST_DATE'.
        <fs_fieldcat>-reptext = 'End of Test Date(DD.MM.YYYY)'.
      WHEN 'GHO_END_OF_TEST_TIME'.
        <fs_fieldcat>-reptext = 'End of Test Time(HH:MM:SS)'.
      WHEN 'QUANTITY'.
        <fs_fieldcat>-reptext = 'Quantity'.
      WHEN 'VALID_FROM_DATE'.
        <fs_fieldcat>-reptext = 'Valid from date( DD.MM.YYYY )'.
      WHEN 'VALID_FROM_TIME'.
        <fs_fieldcat>-reptext = 'Valid from time( HH:MM:SS )'.
      WHEN 'VALID_TO_DATE'.
        <fs_fieldcat>-reptext = 'Valid to date( DD.MM.YYYY )'.
      WHEN 'VALID_TO_TIME'.
        <fs_fieldcat>-reptext = 'Valid to time( HH:MM:SS )'.
    ENDCASE.
  ENDLOOP.
ENDFORM.                    " GET_TEXT
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/774041e6-fd95-20ce-9ddd-a56f1cb02428.png)

</div></details>

###1-4. XLSX用インスタンス作成を生成してダウンロード

####参照した記事
[Excel file (*.xlsx) Export/Import]
(https://blogs.sap.com/2019/06/06/excel-file-.xlsx-exportimport/)

ハイレベルなサンプルプログラムです。
指定したテーブルIDのデータダウンロード（とアップロード（後述））を行う仕様です。

#####ロジックの概要（わかる範囲で…）
・テーブルIDとSQL Scriptから対象データ抽出
・cl_ehfnd_xlsxでインスタンスを取得（生成？）（IF_EHFND_XLSX_DOCを参照）
・インスタンスの各メソッドを使用して、対象データの項目見出しとデータをセットし、Save
・作成されたXSTRINGデータから、type X型の内部テーブルを作成し、ダウンロード

#####サンプルコード

掲載されているコードを丸ごとコピペして、試しに「MARA」を指定すると適切にデータが
作成されていることを確認しました
（コードがバラバラになていますが、それぞれをコピペしてうまくつなげば動きます）。
コピペのみですのでサンプルコードは記載せず、スクショだけ添えておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/2cd64ef5-0c9b-d930-b0e5-97ff997f572b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/edc355df-db98-26b1-7781-59bce943a059.png)

#####注意事項
上記時のプログラムはDL→ULプログラムなので、基本的にはアドオンテーブル用メンテナンス用プログラムとしての
使用を想定しているはずです。標準テーブルの指定はダウンロード時のみとしてください。
また、アップロードの機能については仕様制限があります。後述の、アップロードの記事も参考にしてください。


###1-5. XMLファイルを作成せずにXSTRINGファイルからxlsxファイルを生成

下記のサイトに、IF_SALV_BS_LEX_FORMAT=>MC_FORMAT_XLSXを使用して、内部テーブル→xlsx形式XSTRING形式に変換する
サンプルプログラムがありました。
[Create XLSX file from internal table in background v2]
(https://abapblog.com/articles/tricks/120-create-xlsx-file-from-internal-table-in-background-v2)
これに1.3.にある、XSTRING→バイナリ形式ダウンロードのコードを追加して、動作するようにしました。

<details><summary>サンプルコード(内部テーブル→XMLを経由せずXSTRING→バイナリ)</summary><div>

```abap:SAMPLE03
*&---------------------------------------------------------------------*
*& Report y_test_gohta039
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT Y_TEST_GOHTA039.

"https://abapblog.com/articles/tricks/120-create-xlsx-file-from-internal-table-in-background-v2
CLASS DOWNLOAD_XLSX DEFINITION.
  PUBLIC SECTION.
* static method クラスやインターフェースに依存しないクラスのメソッド
    CLASS-METHODS: MAIN,
                   DOWNLOAD_TO_LOCAL.
    CLASS-DATA: IT_FIELDCAT      TYPE LVC_T_FCAT,
                IT_SORT          TYPE LVC_T_SORT,
                IT_FILT          TYPE LVC_T_FILT,
                IS_LAYOUT        TYPE LVC_S_LAYO,
                IT_HYPERLINKS    TYPE LVC_T_HYPE,
                W_XSTRING        TYPE XSTRING.
ENDCLASS.

CLASS CREATE_XLSX_XSTRING DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS: CREATE_XLSX_FROM_ITAB
      IMPORTING
                IT_FIELDCAT      TYPE LVC_T_FCAT OPTIONAL
                IT_SORT          TYPE LVC_T_SORT OPTIONAL
                IT_FILT          TYPE LVC_T_FILT OPTIONAL
                IS_LAYOUT        TYPE LVC_S_LAYO OPTIONAL
                IT_HYPERLINKS    TYPE LVC_T_HYPE OPTIONAL
                VALUE(IT_DATA)   TYPE STANDARD TABLE
      RETURNING VALUE(R_XSTRING) TYPE XSTRING.
ENDCLASS.

CLASS DOWNLOAD_XLSX IMPLEMENTATION.
  METHOD MAIN.
*   data population
    select from mara as t1
      inner join marc as t2
      on t1~matnr = t2~matnr
      fields
      t1~matnr,
      t2~werks
      order by t1~matnr, t2~werks
      into table @data(it_data)
      up to 30 rows.

    W_XSTRING = CREATE_XLSX_XSTRING=>CREATE_XLSX_FROM_ITAB(
     EXPORTING
       IT_FIELDCAT   = IT_FIELDCAT
       IT_SORT       = IT_SORT
       IT_FILT       = IT_FILT
       IS_LAYOUT     = IS_LAYOUT
       IT_HYPERLINKS = IT_HYPERLINKS
       IT_DATA = IT_DATA ).

    DOWNLOAD_TO_LOCAL(  ).
  ENDMETHOD.

  METHOD DOWNLOAD_TO_LOCAL.
    data: W_FILENAME       TYPE STRING,
          W_PATH           TYPE STRING,
          W_FULLPATH       TYPE STRING,
          W_SIZE           TYPE I,
          IT_BINTAB        TYPE SOLIX_TAB.

    IF W_XSTRING IS NOT INITIAL.
      CL_GUI_FRONTEND_SERVICES=>FILE_SAVE_DIALOG(
       EXPORTING DEFAULT_EXTENSION = 'xlsx'
                 DEFAULT_FILE_NAME = 'INTERNAL_TABLE'
       CHANGING  FILENAME = W_FILENAME
                 PATH     = W_PATH
                 FULLPATH = W_FULLPATH ).
* XSTRING形式のファイルをバイナリ内部テーブル化し、ファイルサイズを返す
      CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
        EXPORTING
          BUFFER        = W_XSTRING
*         APPEND_TO_TABLE = ' '
        IMPORTING
          OUTPUT_LENGTH = W_SIZE
        TABLES
          BINARY_TAB    = IT_BINTAB.
* バイナリファイルのダウンロード
      CL_GUI_FRONTEND_SERVICES=>GUI_DOWNLOAD(
        EXPORTING
          BIN_FILESIZE              = W_SIZE
          FILENAME                  = W_FULLPATH
          FILETYPE                  = 'BIN'
        CHANGING
          DATA_TAB                  = IT_BINTAB ).
      IF SY-SUBRC <> 0.
*     Implement suitable error handling here
      ENDIF.
    ELSE.
      CL_DEMO_OUTPUT=>DISPLAY( |NO DATA| ).
    ENDIF.
  ENDMETHOD.

ENDCLASS.

CLASS CREATE_XLSX_XSTRING IMPLEMENTATION.
  METHOD CREATE_XLSX_FROM_ITAB.
    DATA(LT_DATA) = REF #( IT_DATA ).
    IF IT_FIELDCAT IS INITIAL.
      FIELD-SYMBOLS: <TAB> TYPE STANDARD TABLE.
      ASSIGN LT_DATA->* TO <TAB>.
      TRY.
          CL_SALV_TABLE=>FACTORY(
          EXPORTING
            LIST_DISPLAY = ABAP_FALSE
          IMPORTING
            R_SALV_TABLE = DATA(SALV_TABLE)
          CHANGING
            T_TABLE      = <TAB> ).
          DATA(LT_FCAT) =
            CL_SALV_CONTROLLER_METADATA=>GET_LVC_FIELDCATALOG(
               R_COLUMNS      = SALV_TABLE->GET_COLUMNS( )
               R_AGGREGATIONS = SALV_TABLE->GET_AGGREGATIONS( ) ).
        CATCH CX_SALV_MSG.
          RETURN.
      ENDTRY.
    ELSE.
      LT_FCAT = IT_FIELDCAT.
    ENDIF.

    CL_SALV_BS_LEX=>EXPORT_FROM_RESULT_DATA_TABLE(
      EXPORTING
        IS_FORMAT            = IF_SALV_BS_LEX_FORMAT=>MC_FORMAT_XLSX
        IR_RESULT_DATA_TABLE =
          CL_SALV_EX_UTIL=>FACTORY_RESULT_DATA_TABLE(
            R_DATA         = LT_DATA
            S_LAYOUT       = IS_LAYOUT
            T_FIELDCATALOG = LT_FCAT
            T_SORT         = IT_SORT
            T_FILTER       = IT_FILT
            T_HYPERLINKS   = IT_HYPERLINKS )
      IMPORTING
        ER_RESULT_FILE       = R_XSTRING ).
  ENDMETHOD.

ENDCLASS.

START-OF-SELECTION.
  DOWNLOAD_XLSX=>MAIN( ).
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/ee44a2d6-441e-a730-9426-6aa2d4688edc.png)


</div></details>

##2. Excelアップロード

###2.1. 汎用M：ALSM_EXCEL_TO_INTERNAL_TABLEを使用する

OLEをつかってExcelファイルの値を内部テーブルに取り込むSAP標準汎用モジュールです。
ROW値、COL値、対象値というレイアウトの内部テーブルに格納されるので、
使用したいレイアウトの内部テーブルに取り込む処理が必要です。
制限事項の問題がなければ、非常に適用しやすいと思います。

[OLE（Object Linking and Embedding）とは - IT用語辞典 e-Words]
(http://e-words.jp/w/OLE.html)

####仕様制限
・OLEは処理速度が遅く、テキストファイルのアップロードと比較して処理時間がかかります
・WEB GUIでは使用できません。これは、OLE自体がWEB GUIで使用できないためです。
[EXcel Upload to SAP fails from WEBGUI]
(https://answers.sap.com/questions/6923153/excel-upload-to-sap-fails-from-webgui.html)
※FIORIからの動作可否は未検証です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/75adcdfd-8979-f36e-d104-1c1839355bb1.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/602e86d9-1db0-47d8-adb7-5854fb9c0291.png)

###2.2. 「ABAP to XLSX in less than 10 minutes」

以下のサイトのソースをコピペして試しました。
また、トランザクション：STRANSにて、SIMPLE TRANSFORMATIONのコードを登録しました。

####参照した記事
[ABAP to XLSX in less than 10 minutes]
(https://blogs.sap.com/2014/05/15/abap-to-xlsx-in-less-than-10-minutes/)

結論から言うと、上記のサイトの最後のコメントにあるとおり、Z始まりのクラスが定義されていないことで
構文エラーが発生して動きませんでした。
リンク先の記事には記述ありませんでしたが、サードパーティー製品の機能が必要なのかもしれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/e1f1f670-4a82-a85e-5f69-e1d091cf5267.png)

###2.3. XLSX Upload –a unified approach

####参照した記事
[XLSX Upload –a unified approach]
(https://www.sap.com/documents/2015/07/ced1ac7f-527c-0010-82c7-eda71af511fa.html)

まずこちらのサイトの記事では、
拡張子「xlsx」のファイルがZIPファイル圧縮ファイルであり、数値とテキストが別XMLファイルに
格納されているので、それらを紐づけてデータを取得するロジックとする、という仕様を説明しています。
また、XLSTを使用した変換処理も行っています
（SIMPLE TRANSFORMATIONと同様に、トランザクション：STRANSで設定）。

結果としては、動作はするものの、適切な結果は得られませんでした。
XLSXファイルの内容をXMLファイルからうまく取得しているのですが、
それらを内部テーブルに設定するときに、値の項目取得で失敗する箇所があり、
データをうまく内部テーブル化できませんでした。
かなり時間をかけて調査をしたのですが、原因が確認できず断念しました。

###2.4. 1.4のコードにあるアップロード機能

####参照した記事
[Excel file (*.xlsx) Export/Import]
(https://blogs.sap.com/2019/06/06/excel-file-.xlsx-exportimport/)

こちらのアップロード機能を試してみました。
動作確認のため、テーブルIDに「MARA」を指定し、ダウンロード機能で作った「MARA]のデータを取り込みました。
ただしキー項目の内容は取得できているのですが、アップロードファイルのキー項目以降の項目がない、という
エラーが発生しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/256390a6-ba72-8c27-df61-8301dcc786a6.png)

調査をしたところ、テーブルコンポーネントの情報を取得するところで、
INCLUDEを参照して定義している項目が取得できないため、そのあとの項目値判定処理で
エラーとなることが分かりました。
※画面上のMARAはキー項目のすぐ後からINCLUDEなのですが、VBAKなどでも確認済み。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/599d58c7-b33d-cece-bc06-77ba138324d0.png)

そのため、現状でこの機能が適切に動くのは、
・項目をインクルードしていないアドオンテーブル（※）
に限られるものと思われます。
(※）標準テーブルをデータダウンロードアップロードで更新してはいけません。
ただ、標準テーブルはINCLUDEを使用しているテーブルが多いのでエラーになるはずですが。

##まとめ

###ダウンロード
1-3.の方法が難易度やコード量的に現状一番わかりやすい対処方法になると考えています。
1-4.も利用が進めば十分候補になると思います。セルのプロパティの設定などもできるようです
（するかは別です！）。

###アップロード
WEBGUIでの制限はあるものの、2-1.が難易度が低く、実装工数的にも実用的と考えています。

ほかのサンプルコードは制限事項とは別に、

・コード量や難易度が高い
・トランザクション：STRANSでのXSLTのコード作成が必要となる

など、プロジェクト導入という観点で考えると超える壁があります
（CALL TRANSFORMATIONなんてアドオンで書いたりしています？）


最初にも書きましたが、とにかく奥が深く、難解なコードの調査には相当手間取りました。
アップロードはコードの作りが異なるものが複数あり対処方法が安定していない現状も把握できました。
趣味でやっておいてよかったです。業務でやっていたら相当辛い日々だったのではないかなと…:sweat_smile:

今回のサンプルコードを調査検証し、簡単なラッパクラスを作ることができれば、
技術ソリューションとして提示することも可能になるものと考えています
（すでにいろいろな方がご尽力されていて、いろいろな環境に適用されているとは思いますが）。

今回の内容は以上です、ありがとうございました。