Excel-Verarbeitung in ABAP Cloud unterscheidet sich von klassischem ABAP. Legacy-Klassen wie CL_ABAP_CONV_* mit Excel-spezifischen Formaten oder GUI-basierte Downloads stehen nicht zur Verfuegung. Stattdessen nutzt du Cloud-native APIs wie XCO, die XLSX-Bibliothek oder externe Services.
Verfuegbare Optionen im Ueberblick
In ABAP Cloud gibt es mehrere Wege fuer Excel-Verarbeitung:
| Option | Beschreibung | Anwendungsfall |
|---|---|---|
| XCO Spreadsheet API | Cloud-native SAP API | Standard-Excel-Export |
| XLSX Bibliothek | Open-Source Bibliothek | Erweiterte Formatierung |
| Externe Services | HTTP-basierte APIs | Spezielle Konvertierungen |
| CSV als Alternative | Einfaches Textformat | Maximale Kompatibilitaet |
Architektur-Uebersicht
┌─────────────────────────────────────────────────────────────────────────────┐│ Excel-Verarbeitung in ABAP Cloud ││ ││ ┌────────────────────────────────────────────────────────────────────────┐ ││ │ RAP Business Object / Service │ ││ │ │ ││ │ ┌─────────────────────┐ ┌─────────────────────┐ │ ││ │ │ RAP Action │ │ RAP Action │ │ ││ │ │ "Export Excel" │ │ "Import Excel" │ │ ││ │ └──────────┬──────────┘ └──────────┬──────────┘ │ ││ └─────────────┼────────────────────────┼──────────────────────────────────┘ ││ │ │ ││ ▼ ▼ ││ ┌─────────────────────────────────────────────────────────────────────────┐ ││ │ Excel-Verarbeitungs-Optionen │ ││ │ │ ││ │ ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ │ ││ │ │ XCO Spreadsheet │ │ XLSX Bibliothek │ │ CSV Fallback │ │ ││ │ │ │ │ │ │ │ │ ││ │ │ - Export/Import │ │ - Formatierung │ │ - Einfach │ │ ││ │ │ - Cloud-native │ │ - Multiple Sheets│ │ - Universell │ │ ││ │ └───────────────────┘ └───────────────────┘ └───────────────────┘ │ ││ └─────────────────────────────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────────────────────────────┘Excel-Export mit XCO
Die XCO (Extension Components) Library bietet eine Cloud-kompatible API fuer Spreadsheet-Operationen.
Grundlegender Export
" Excel-Export mit XCOCLASS zcl_excel_exporter DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_sales_order, order_id TYPE string, customer TYPE string, amount TYPE p LENGTH 10 DECIMALS 2, currency TYPE string, order_date TYPE d, status TYPE string, END OF ty_sales_order, ty_sales_orders TYPE STANDARD TABLE OF ty_sales_order WITH KEY order_id.
METHODS: export_to_excel IMPORTING it_data TYPE ty_sales_orders RETURNING VALUE(rv_xlsx) TYPE xstring RAISING cx_xco_runtime_exception.
ENDCLASS.
CLASS zcl_excel_exporter IMPLEMENTATION.
METHOD export_to_excel. " Spreadsheet-Dokument erstellen DATA(lo_spreadsheet) = xco_cp_xlsx=>document->empty( )->spreadsheet.
" Erstes Worksheet (Standard: Sheet1) DATA(lo_worksheet) = lo_spreadsheet->worksheet->at_position( 1 ).
" Kopfzeile schreiben DATA(lo_cursor) = lo_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( 1 ) ).
lo_cursor->get_cell( )->value->write_from( 'Auftragsnummer' ). lo_cursor->move_right( )->get_cell( )->value->write_from( 'Kunde' ). lo_cursor->move_right( )->get_cell( )->value->write_from( 'Betrag' ). lo_cursor->move_right( )->get_cell( )->value->write_from( 'Waehrung' ). lo_cursor->move_right( )->get_cell( )->value->write_from( 'Bestelldatum' ). lo_cursor->move_right( )->get_cell( )->value->write_from( 'Status' ).
" Datenzeilen schreiben DATA(lv_row) = 2.
LOOP AT it_data INTO DATA(ls_order). lo_cursor = lo_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row ) ).
lo_cursor->get_cell( )->value->write_from( ls_order-order_id ). lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-customer ). lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-amount ). lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-currency ). lo_cursor->move_right( )->get_cell( )->value->write_from( |{ ls_order-order_date DATE = USER }| ). lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-status ).
lv_row = lv_row + 1. ENDLOOP.
" Als XLSX-Binary exportieren rv_xlsx = lo_spreadsheet->get_file_content( ). ENDMETHOD.
ENDCLASS.Generischer Export aus beliebigen Tabellen
" Generischer Excel-Exporter fuer beliebige DatenCLASS zcl_generic_excel_export DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. METHODS: export_table IMPORTING it_data TYPE ANY TABLE it_column_names TYPE string_table OPTIONAL RETURNING VALUE(rv_xlsx) TYPE xstring RAISING cx_xco_runtime_exception.
PRIVATE SECTION. METHODS: get_field_names IMPORTING it_data TYPE ANY TABLE RETURNING VALUE(rt_names) TYPE string_table.
ENDCLASS.
CLASS zcl_generic_excel_export IMPLEMENTATION.
METHOD export_table. DATA(lo_spreadsheet) = xco_cp_xlsx=>document->empty( )->spreadsheet. DATA(lo_worksheet) = lo_spreadsheet->worksheet->at_position( 1 ).
" Feldnamen ermitteln DATA(lt_fields) = COND #( WHEN it_column_names IS NOT INITIAL THEN it_column_names ELSE get_field_names( it_data ) ).
" Kopfzeile DATA(lo_cursor) = lo_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( 1 ) ).
LOOP AT lt_fields INTO DATA(lv_field_name). lo_cursor->get_cell( )->value->write_from( lv_field_name ). IF sy-tabix < lines( lt_fields ). lo_cursor->move_right( ). ENDIF. ENDLOOP.
" Datenzeilen DATA(lv_row) = 2. DATA lv_value TYPE string.
LOOP AT it_data ASSIGNING FIELD-SYMBOL(<ls_row>). lo_cursor = lo_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row ) ).
LOOP AT lt_fields INTO lv_field_name. ASSIGN COMPONENT lv_field_name OF STRUCTURE <ls_row> TO FIELD-SYMBOL(<lv_value>). IF sy-subrc = 0. lv_value = <lv_value>. lo_cursor->get_cell( )->value->write_from( lv_value ). ENDIF.
IF sy-tabix < lines( lt_fields ). lo_cursor->move_right( ). ENDIF. ENDLOOP.
lv_row = lv_row + 1. ENDLOOP.
rv_xlsx = lo_spreadsheet->get_file_content( ). ENDMETHOD.
METHOD get_field_names. " Feldnamen aus Tabellenstruktur ermitteln DATA(lo_table_descr) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( it_data ) ). DATA(lo_struct_descr) = CAST cl_abap_structdescr( lo_table_descr->get_table_line_type( ) ).
LOOP AT lo_struct_descr->get_components( ) INTO DATA(ls_component). APPEND ls_component-name TO rt_names. ENDLOOP. ENDMETHOD.
ENDCLASS.Excel-Import mit Parsing
Der Import von Excel-Dateien erfordert das Parsen der XLSX-Struktur.
XLSX-Import Grundlagen
" Excel-Import ParserCLASS zcl_excel_importer DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_sales_order_import, order_id TYPE string, customer TYPE string, amount TYPE string, currency TYPE string, order_date TYPE string, status TYPE string, END OF ty_sales_order_import, ty_sales_order_imports TYPE STANDARD TABLE OF ty_sales_order_import WITH KEY order_id.
METHODS: import_from_excel IMPORTING iv_xlsx TYPE xstring iv_skip_header TYPE abap_bool DEFAULT abap_true RETURNING VALUE(rt_data) TYPE ty_sales_order_imports RAISING cx_xco_runtime_exception.
METHODS: validate_import IMPORTING it_data TYPE ty_sales_order_imports EXPORTING et_errors TYPE string_table RETURNING VALUE(rv_valid) TYPE abap_bool.
ENDCLASS.
CLASS zcl_excel_importer IMPLEMENTATION.
METHOD import_from_excel. " XLSX-Dokument laden DATA(lo_document) = xco_cp_xlsx=>document->for_file_content( iv_xlsx ). DATA(lo_spreadsheet) = lo_document->spreadsheet.
" Erstes Worksheet lesen DATA(lo_worksheet) = lo_spreadsheet->worksheet->at_position( 1 ).
" Verwendeten Bereich ermitteln DATA(lo_used_area) = lo_worksheet->used_area( ). DATA(lo_row_selection) = lo_used_area->row_selection. DATA(lv_first_row) = lo_row_selection->first_row->position->value. DATA(lv_last_row) = lo_row_selection->last_row->position->value.
" Startzeile (Header ueberspringen) DATA(lv_start_row) = COND #( WHEN iv_skip_header = abap_true THEN lv_first_row + 1 ELSE lv_first_row ).
" Zeilen durchlaufen DATA lv_row TYPE i.
DO ( lv_last_row - lv_start_row + 1 ) TIMES. lv_row = lv_start_row + sy-index - 1.
DATA(lo_cursor) = lo_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row ) ).
DATA ls_order TYPE ty_sales_order_import.
" Zellwerte lesen lo_cursor->get_cell( )->value->read_to( REF #( ls_order-order_id ) ). lo_cursor->move_right( )->get_cell( )->value->read_to( REF #( ls_order-customer ) ). lo_cursor->move_right( )->get_cell( )->value->read_to( REF #( ls_order-amount ) ). lo_cursor->move_right( )->get_cell( )->value->read_to( REF #( ls_order-currency ) ). lo_cursor->move_right( )->get_cell( )->value->read_to( REF #( ls_order-order_date ) ). lo_cursor->move_right( )->get_cell( )->value->read_to( REF #( ls_order-status ) ).
" Leere Zeilen ueberspringen IF ls_order-order_id IS NOT INITIAL. APPEND ls_order TO rt_data. ENDIF. ENDDO. ENDMETHOD.
METHOD validate_import. rv_valid = abap_true. CLEAR et_errors.
LOOP AT it_data INTO DATA(ls_order). DATA(lv_row) = sy-tabix + 1. " +1 fuer Header
" Pflichtfelder pruefen IF ls_order-order_id IS INITIAL. APPEND |Zeile { lv_row }: Auftragsnummer fehlt| TO et_errors. rv_valid = abap_false. ENDIF.
IF ls_order-customer IS INITIAL. APPEND |Zeile { lv_row }: Kunde fehlt| TO et_errors. rv_valid = abap_false. ENDIF.
" Betrag validieren TRY. DATA(lv_amount) = CONV decfloat34( ls_order-amount ). IF lv_amount < 0. APPEND |Zeile { lv_row }: Betrag darf nicht negativ sein| TO et_errors. rv_valid = abap_false. ENDIF. CATCH cx_sy_conversion_no_number. APPEND |Zeile { lv_row }: Ungueliger Betrag '{ ls_order-amount }'| TO et_errors. rv_valid = abap_false. ENDTRY.
" Waehrung validieren IF ls_order-currency NOT IN VALUE #( ( sign = 'I' option = 'EQ' low = 'EUR' ) ( sign = 'I' option = 'EQ' low = 'USD' ) ( sign = 'I' option = 'EQ' low = 'CHF' ) ). APPEND |Zeile { lv_row }: Ungueltige Waehrung '{ ls_order-currency }'| TO et_errors. rv_valid = abap_false. ENDIF. ENDLOOP. ENDMETHOD.
ENDCLASS.Generischer Import mit dynamischer Struktur
" Generischer Excel-Import in interne TabelleCLASS zcl_generic_excel_import DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_cell, column TYPE i, row TYPE i, value TYPE string, END OF ty_cell, ty_cells TYPE STANDARD TABLE OF ty_cell WITH KEY row column.
METHODS: read_worksheet_raw IMPORTING iv_xlsx TYPE xstring iv_worksheet_pos TYPE i DEFAULT 1 RETURNING VALUE(rt_cells) TYPE ty_cells RAISING cx_xco_runtime_exception.
METHODS: read_as_table IMPORTING iv_xlsx TYPE xstring iv_worksheet_pos TYPE i DEFAULT 1 iv_has_header TYPE abap_bool DEFAULT abap_true CHANGING ct_data TYPE ANY TABLE RAISING cx_xco_runtime_exception.
ENDCLASS.
CLASS zcl_generic_excel_import IMPLEMENTATION.
METHOD read_worksheet_raw. DATA(lo_document) = xco_cp_xlsx=>document->for_file_content( iv_xlsx ). DATA(lo_worksheet) = lo_document->spreadsheet->worksheet->at_position( iv_worksheet_pos ).
DATA(lo_used_area) = lo_worksheet->used_area( ).
" Bereich ermitteln DATA(lv_first_row) = lo_used_area->row_selection->first_row->position->value. DATA(lv_last_row) = lo_used_area->row_selection->last_row->position->value. DATA(lv_first_col) = lo_used_area->column_selection->first_column->position->value. DATA(lv_last_col) = lo_used_area->column_selection->last_column->position->value.
" Alle Zellen lesen DO ( lv_last_row - lv_first_row + 1 ) TIMES. DATA(lv_row) = lv_first_row + sy-index - 1.
DO ( lv_last_col - lv_first_col + 1 ) TIMES. DATA(lv_col) = lv_first_col + sy-index - 1.
DATA(lo_cursor) = lo_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_numeric_value( lv_col ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row ) ).
DATA lv_value TYPE string. lo_cursor->get_cell( )->value->read_to( REF #( lv_value ) ).
APPEND VALUE #( column = lv_col row = lv_row value = lv_value ) TO rt_cells. ENDDO. ENDDO. ENDMETHOD.
METHOD read_as_table. " Rohdaten lesen DATA(lt_cells) = read_worksheet_raw( iv_xlsx = iv_xlsx iv_worksheet_pos = iv_worksheet_pos ).
" Spaltenstruktur ermitteln DATA(lo_table_descr) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( ct_data ) ). DATA(lo_struct_descr) = CAST cl_abap_structdescr( lo_table_descr->get_table_line_type( ) ). DATA(lt_components) = lo_struct_descr->get_components( ).
" Header-Mapping erstellen (wenn vorhanden) DATA lt_column_mapping TYPE TABLE OF ty_cell.
IF iv_has_header = abap_true. lt_column_mapping = VALUE #( FOR ls_cell IN lt_cells WHERE ( row = 1 ) ( ls_cell ) ). ENDIF.
" Startzeile bestimmen DATA(lv_start_row) = COND #( WHEN iv_has_header = abap_true THEN 2 ELSE 1 ).
" Eindeutige Zeilen ermitteln DATA lt_rows TYPE SORTED TABLE OF i WITH UNIQUE KEY table_line. LOOP AT lt_cells INTO DATA(ls_cell) WHERE row >= lv_start_row. INSERT ls_cell-row INTO TABLE lt_rows. ENDLOOP.
" Daten einlesen LOOP AT lt_rows INTO DATA(lv_row). DATA ls_line TYPE REF TO data. CREATE DATA ls_line LIKE LINE OF ct_data. ASSIGN ls_line->* TO FIELD-SYMBOL(<ls_line>).
LOOP AT lt_cells INTO ls_cell WHERE row = lv_row. " Spalte zu Feldname mappen DATA(lv_field_index) = ls_cell-column.
IF iv_has_header = abap_true. " Feldname aus Header READ TABLE lt_column_mapping INTO DATA(ls_header) WITH KEY column = ls_cell-column. IF sy-subrc = 0. READ TABLE lt_components WITH KEY name = to_upper( ls_header-value ) TRANSPORTING NO FIELDS. IF sy-subrc = 0. lv_field_index = sy-tabix. ENDIF. ENDIF. ENDIF.
IF lv_field_index <= lines( lt_components ). ASSIGN COMPONENT lv_field_index OF STRUCTURE <ls_line> TO FIELD-SYMBOL(<lv_field>). IF sy-subrc = 0. <lv_field> = ls_cell-value. ENDIF. ENDIF. ENDLOOP.
APPEND <ls_line> TO ct_data. ENDLOOP. ENDMETHOD.
ENDCLASS.Formatierung (Farben, Spaltenbreite)
Fuer erweiterte Formatierung kannst du Zellenstile definieren.
Formatierter Export
" Excel-Export mit FormatierungCLASS zcl_formatted_excel_export DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. METHODS: export_with_formatting IMPORTING it_data TYPE zcl_excel_exporter=>ty_sales_orders RETURNING VALUE(rv_xlsx) TYPE xstring RAISING cx_xco_runtime_exception.
PRIVATE SECTION. METHODS: apply_header_style IMPORTING io_cursor TYPE REF TO if_xco_xlsx_wa_cursor.
METHODS: apply_amount_style IMPORTING io_cursor TYPE REF TO if_xco_xlsx_wa_cursor iv_amount TYPE p.
METHODS: apply_status_style IMPORTING io_cursor TYPE REF TO if_xco_xlsx_wa_cursor iv_status TYPE string.
ENDCLASS.
CLASS zcl_formatted_excel_export IMPLEMENTATION.
METHOD export_with_formatting. DATA(lo_spreadsheet) = xco_cp_xlsx=>document->empty( )->spreadsheet. DATA(lo_worksheet) = lo_spreadsheet->worksheet->at_position( 1 ).
" Worksheet-Namen setzen lo_worksheet->properties( )->name->set( 'Auftraege' ).
" Kopfzeile mit Formatierung DATA(lo_cursor) = lo_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( 1 ) ).
DATA(lt_headers) = VALUE string_table( ( `Auftragsnummer` ) ( `Kunde` ) ( `Betrag` ) ( `Waehrung` ) ( `Bestelldatum` ) ( `Status` ) ).
LOOP AT lt_headers INTO DATA(lv_header). lo_cursor->get_cell( )->value->write_from( lv_header ). apply_header_style( lo_cursor ).
IF sy-tabix < lines( lt_headers ). lo_cursor->move_right( ). ENDIF. ENDLOOP.
" Datenzeilen mit bedingter Formatierung DATA(lv_row) = 2.
LOOP AT it_data INTO DATA(ls_order). lo_cursor = lo_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row ) ).
" Auftragsnummer lo_cursor->get_cell( )->value->write_from( ls_order-order_id ).
" Kunde lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-customer ).
" Betrag mit Formatierung lo_cursor->move_right( ). lo_cursor->get_cell( )->value->write_from( ls_order-amount ). apply_amount_style( io_cursor = lo_cursor iv_amount = ls_order-amount ).
" Waehrung lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-currency ).
" Datum lo_cursor->move_right( )->get_cell( )->value->write_from( |{ ls_order-order_date DATE = USER }| ).
" Status mit Farbcodierung lo_cursor->move_right( ). lo_cursor->get_cell( )->value->write_from( ls_order-status ). apply_status_style( io_cursor = lo_cursor iv_status = ls_order-status ).
lv_row = lv_row + 1. ENDLOOP.
rv_xlsx = lo_spreadsheet->get_file_content( ). ENDMETHOD.
METHOD apply_header_style. " Kopfzeile: Fett und Hintergrundfarbe " Hinweis: Die XCO API bietet begrenzte Style-Optionen " Fuer erweiterte Formatierung siehe XLSX-Bibliothek
" Beispiel: Zellbreite anpassen (wo verfuegbar) " io_cursor->get_cell( )->style->set_bold( abap_true ). ENDMETHOD.
METHOD apply_amount_style. " Betraege > 10000 hervorheben IF iv_amount > 10000. " Hier wuerde Formatierung angewendet werden ENDIF. ENDMETHOD.
METHOD apply_status_style. " Statusabhängige Farbcodierung CASE iv_status. WHEN 'COMPLETED'. " Gruen WHEN 'PENDING'. " Gelb WHEN 'CANCELLED'. " Rot ENDCASE. ENDMETHOD.
ENDCLASS.Erweiterte Formatierung mit XLSX-Struktur
Fuer komplexere Formatierungen kannst du direkt mit der XLSX-Struktur arbeiten:
" XLSX-Manipulator fuer erweiterte FormatierungCLASS zcl_xlsx_formatter DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_column_format, column_letter TYPE c LENGTH 3, width TYPE i, format TYPE string, " z.B. '#,##0.00' fuer Zahlen bold_header TYPE abap_bool, END OF ty_column_format, ty_column_formats TYPE STANDARD TABLE OF ty_column_format WITH KEY column_letter.
METHODS: create_formatted_export IMPORTING it_data TYPE ANY TABLE it_column_formats TYPE ty_column_formats OPTIONAL iv_title TYPE string OPTIONAL RETURNING VALUE(rv_xlsx) TYPE xstring RAISING cx_xco_runtime_exception.
PRIVATE SECTION. METHODS: column_index_to_letter IMPORTING iv_index TYPE i RETURNING VALUE(rv_letter) TYPE string.
ENDCLASS.
CLASS zcl_xlsx_formatter IMPLEMENTATION.
METHOD create_formatted_export. DATA(lo_spreadsheet) = xco_cp_xlsx=>document->empty( )->spreadsheet. DATA(lo_worksheet) = lo_spreadsheet->worksheet->at_position( 1 ).
" Titel setzen wenn angegeben IF iv_title IS NOT INITIAL. lo_worksheet->properties( )->name->set( iv_title ). ENDIF.
" Tabellenstruktur ermitteln DATA(lo_table_descr) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( it_data ) ). DATA(lo_struct_descr) = CAST cl_abap_structdescr( lo_table_descr->get_table_line_type( ) ). DATA(lt_components) = lo_struct_descr->get_components( ).
" Kopfzeile DATA(lo_cursor) = lo_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( 1 ) ).
LOOP AT lt_components INTO DATA(ls_component). lo_cursor->get_cell( )->value->write_from( ls_component-name ). IF sy-tabix < lines( lt_components ). lo_cursor->move_right( ). ENDIF. ENDLOOP.
" Datenzeilen DATA(lv_row) = 2. DATA lv_value TYPE string.
LOOP AT it_data ASSIGNING FIELD-SYMBOL(<ls_row>). lo_cursor = lo_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row ) ).
LOOP AT lt_components INTO ls_component. ASSIGN COMPONENT ls_component-name OF STRUCTURE <ls_row> TO FIELD-SYMBOL(<lv_field>).
IF sy-subrc = 0. " Typabhaengige Formatierung CASE ls_component-type->type_kind. WHEN cl_abap_typedescr=>typekind_date. lv_value = |{ CONV d( <lv_field> ) DATE = USER }|. WHEN cl_abap_typedescr=>typekind_time. lv_value = |{ CONV t( <lv_field> ) TIME = USER }|. WHEN cl_abap_typedescr=>typekind_packed OR cl_abap_typedescr=>typekind_decfloat OR cl_abap_typedescr=>typekind_decfloat16 OR cl_abap_typedescr=>typekind_decfloat34. lv_value = |{ <lv_field> }|. WHEN OTHERS. lv_value = <lv_field>. ENDCASE.
lo_cursor->get_cell( )->value->write_from( lv_value ). ENDIF.
IF sy-tabix < lines( lt_components ). lo_cursor->move_right( ). ENDIF. ENDLOOP.
lv_row = lv_row + 1. ENDLOOP.
rv_xlsx = lo_spreadsheet->get_file_content( ). ENDMETHOD.
METHOD column_index_to_letter. " 1 -> A, 2 -> B, ..., 26 -> Z, 27 -> AA DATA(lv_remaining) = iv_index.
WHILE lv_remaining > 0. DATA(lv_mod) = ( lv_remaining - 1 ) MOD 26. rv_letter = |{ CONV c( lv_mod + 65 ) }{ rv_letter }|. lv_remaining = ( lv_remaining - lv_mod - 1 ) / 26. ENDWHILE. ENDMETHOD.
ENDCLASS.Multiple Worksheets
Fuer komplexere Reports mit mehreren Tabellenblaettern.
Export mit mehreren Worksheets
" Multi-Worksheet Excel ExportCLASS zcl_multi_worksheet_export DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_worksheet_data, name TYPE string, data TYPE REF TO data, " Referenz auf ANY TABLE END OF ty_worksheet_data, ty_worksheets TYPE STANDARD TABLE OF ty_worksheet_data WITH KEY name.
METHODS: export_multiple_worksheets IMPORTING it_worksheets TYPE ty_worksheets RETURNING VALUE(rv_xlsx) TYPE xstring RAISING cx_xco_runtime_exception.
PRIVATE SECTION. METHODS: write_table_to_worksheet IMPORTING io_worksheet TYPE REF TO if_xco_xlsx_wa_worksheet it_data TYPE ANY TABLE.
ENDCLASS.
CLASS zcl_multi_worksheet_export IMPLEMENTATION.
METHOD export_multiple_worksheets. DATA(lo_spreadsheet) = xco_cp_xlsx=>document->empty( )->spreadsheet.
DATA(lv_sheet_pos) = 1.
LOOP AT it_worksheets INTO DATA(ls_worksheet). " Worksheet erstellen/holen DATA(lo_worksheet) = lo_spreadsheet->worksheet->at_position( lv_sheet_pos ).
" Name setzen lo_worksheet->properties( )->name->set( ls_worksheet-name ).
" Daten schreiben ASSIGN ls_worksheet-data->* TO FIELD-SYMBOL(<lt_data>). write_table_to_worksheet( io_worksheet = lo_worksheet it_data = <lt_data> ).
lv_sheet_pos = lv_sheet_pos + 1. ENDLOOP.
rv_xlsx = lo_spreadsheet->get_file_content( ). ENDMETHOD.
METHOD write_table_to_worksheet. " Struktur ermitteln DATA(lo_table_descr) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( it_data ) ). DATA(lo_struct_descr) = CAST cl_abap_structdescr( lo_table_descr->get_table_line_type( ) ). DATA(lt_components) = lo_struct_descr->get_components( ).
" Kopfzeile DATA(lo_cursor) = io_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( 1 ) ).
LOOP AT lt_components INTO DATA(ls_component). lo_cursor->get_cell( )->value->write_from( ls_component-name ). IF sy-tabix < lines( lt_components ). lo_cursor->move_right( ). ENDIF. ENDLOOP.
" Daten DATA(lv_row) = 2. DATA lv_value TYPE string.
LOOP AT it_data ASSIGNING FIELD-SYMBOL(<ls_row>). lo_cursor = io_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row ) ).
LOOP AT lt_components INTO ls_component. ASSIGN COMPONENT ls_component-name OF STRUCTURE <ls_row> TO FIELD-SYMBOL(<lv_field>). IF sy-subrc = 0. lv_value = <lv_field>. lo_cursor->get_cell( )->value->write_from( lv_value ). ENDIF. IF sy-tabix < lines( lt_components ). lo_cursor->move_right( ). ENDIF. ENDLOOP.
lv_row = lv_row + 1. ENDLOOP. ENDMETHOD.
ENDCLASS.Praktisches Beispiel: Reporting mit mehreren Sheets
" Monatsreport mit Summary und DetailsCLASS zcl_monthly_report_export DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_summary, month TYPE string, total_orders TYPE i, total_revenue TYPE p LENGTH 10 DECIMALS 2, avg_order_size TYPE p LENGTH 10 DECIMALS 2, END OF ty_summary, ty_summaries TYPE STANDARD TABLE OF ty_summary WITH KEY month.
TYPES: BEGIN OF ty_order_detail, order_id TYPE string, order_date TYPE d, customer TYPE string, amount TYPE p LENGTH 10 DECIMALS 2, status TYPE string, END OF ty_order_detail, ty_order_details TYPE STANDARD TABLE OF ty_order_detail WITH KEY order_id.
METHODS: generate_report IMPORTING it_summary TYPE ty_summaries it_details TYPE ty_order_details RETURNING VALUE(rv_xlsx) TYPE xstring RAISING cx_xco_runtime_exception.
ENDCLASS.
CLASS zcl_monthly_report_export IMPLEMENTATION.
METHOD generate_report. " Worksheets vorbereiten DATA lt_worksheets TYPE zcl_multi_worksheet_export=>ty_worksheets.
" Summary-Daten DATA lr_summary TYPE REF TO ty_summaries. CREATE DATA lr_summary. lr_summary->* = it_summary.
APPEND VALUE #( name = 'Zusammenfassung' data = lr_summary ) TO lt_worksheets.
" Detail-Daten DATA lr_details TYPE REF TO ty_order_details. CREATE DATA lr_details. lr_details->* = it_details.
APPEND VALUE #( name = 'Auftragsdetails' data = lr_details ) TO lt_worksheets.
" Export durchfuehren DATA(lo_exporter) = NEW zcl_multi_worksheet_export( ). rv_xlsx = lo_exporter->export_multiple_worksheets( lt_worksheets ). ENDMETHOD.
ENDCLASS.Integration in RAP Action
Die nahtlose Integration in RAP-Business-Objects ermoeglicht Excel-Download direkt aus Fiori-Apps.
Behavior Definition
managed implementation in class ZBP_I_SALESORDER unique;strict ( 2 );
define behavior for ZI_SalesOrder alias SalesOrderpersistent table zsalesorderlock masterauthorization master ( instance ){ create; update; delete;
// Excel-Export als Action static action exportToExcel result [1] ZA_ExcelDownload;
// Excel-Import als Action mit Parameter static action importFromExcel parameter ZA_ExcelUpload result [*] $self;
field ( readonly ) OrderUUID, CreatedAt, CreatedBy;}Action Parameter und Result
-- Excel-Download Result@EndUserText.label: 'Excel Download Result'define abstract entity ZA_ExcelDownload{ FileName : abap.char(100); MimeType : abap.char(50); FileContent : abap.rawstring(0);}
-- Excel-Upload Parameter@EndUserText.label: 'Excel Upload Parameter'define abstract entity ZA_ExcelUpload{ FileName : abap.char(100); MimeType : abap.char(50); FileContent : abap.rawstring(0);}Behavior Implementation
" RAP Behavior Implementation mit Excel-ActionsCLASS lhc_salesorder DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION. METHODS exportToExcel FOR MODIFY IMPORTING keys FOR ACTION SalesOrder~exportToExcel RESULT result.
METHODS importFromExcel FOR MODIFY IMPORTING keys FOR ACTION SalesOrder~importFromExcel RESULT result.
ENDCLASS.
CLASS lhc_salesorder IMPLEMENTATION.
METHOD exportToExcel. " Alle Auftraege laden SELECT order_id, customer, amount, currency, order_date, status FROM zsalesorder INTO TABLE @DATA(lt_orders).
" In Export-Format konvertieren DATA lt_export TYPE zcl_excel_exporter=>ty_sales_orders. lt_export = CORRESPONDING #( lt_orders ).
TRY. " Excel generieren DATA(lo_exporter) = NEW zcl_excel_exporter( ). DATA(lv_xlsx) = lo_exporter->export_to_excel( lt_export ).
" Dateiname mit Timestamp DATA(lv_filename) = |Auftraege_{ sy-datum }_{ sy-uzeit }.xlsx|.
" Result zurueckgeben APPEND VALUE #( %cid = keys[ 1 ]-%cid %param = VALUE #( FileName = lv_filename MimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' FileContent = lv_xlsx ) ) TO result.
CATCH cx_xco_runtime_exception INTO DATA(lx_error). APPEND VALUE #( %cid = keys[ 1 ]-%cid %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = lx_error->get_text( ) ) ) TO reported-salesorder. ENDTRY. ENDMETHOD.
METHOD importFromExcel. " Parameter auslesen DATA(ls_param) = keys[ 1 ]-%param.
TRY. " Excel parsen DATA(lo_importer) = NEW zcl_excel_importer( ). DATA(lt_imported) = lo_importer->import_from_excel( iv_xlsx = ls_param-FileContent iv_skip_header = abap_true ).
" Validierung DATA lt_errors TYPE string_table. DATA(lv_valid) = lo_importer->validate_import( EXPORTING it_data = lt_imported IMPORTING et_errors = lt_errors ).
IF lv_valid = abap_false. " Fehler melden LOOP AT lt_errors INTO DATA(lv_error). APPEND VALUE #( %cid = keys[ 1 ]-%cid %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = lv_error ) ) TO reported-salesorder. ENDLOOP. RETURN. ENDIF.
" Daten importieren DATA lt_create TYPE TABLE FOR CREATE zi_salesorder.
LOOP AT lt_imported INTO DATA(ls_imported). APPEND VALUE #( %cid = |IMPORT_{ sy-tabix }| OrderID = ls_imported-order_id Customer = ls_imported-customer Amount = CONV #( ls_imported-amount ) Currency = ls_imported-currency OrderDate = CONV d( ls_imported-order_date ) Status = ls_imported-status ) TO lt_create. ENDLOOP.
" Entitaeten erstellen MODIFY ENTITIES OF zi_salesorder IN LOCAL MODE ENTITY SalesOrder CREATE SET FIELDS WITH lt_create MAPPED DATA(lt_mapped) FAILED DATA(lt_failed) REPORTED DATA(lt_reported).
" Ergebnis zurueckgeben result = CORRESPONDING #( lt_mapped-salesorder ).
CATCH cx_xco_runtime_exception INTO DATA(lx_error). APPEND VALUE #( %cid = keys[ 1 ]-%cid %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = lx_error->get_text( ) ) ) TO reported-salesorder. ENDTRY. ENDMETHOD.
ENDCLASS.Alternativen: CSV und OData
CSV als einfache Alternative
CSV ist universell kompatibel und einfacher zu generieren:
" CSV-Export als Excel-AlternativeCLASS zcl_csv_exporter DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. CONSTANTS: gc_separator TYPE c LENGTH 1 VALUE ';', gc_newline TYPE string VALUE cl_abap_char_utilities=>cr_lf.
METHODS: export_to_csv IMPORTING it_data TYPE ANY TABLE iv_separator TYPE c DEFAULT gc_separator RETURNING VALUE(rv_csv) TYPE string.
METHODS: export_to_csv_binary IMPORTING it_data TYPE ANY TABLE iv_separator TYPE c DEFAULT gc_separator iv_encoding TYPE string DEFAULT 'UTF-8' RETURNING VALUE(rv_binary) TYPE xstring.
ENDCLASS.
CLASS zcl_csv_exporter IMPLEMENTATION.
METHOD export_to_csv. " Struktur ermitteln DATA(lo_table_descr) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( it_data ) ). DATA(lo_struct_descr) = CAST cl_abap_structdescr( lo_table_descr->get_table_line_type( ) ). DATA(lt_components) = lo_struct_descr->get_components( ).
" Kopfzeile LOOP AT lt_components INTO DATA(ls_component). rv_csv = rv_csv && ls_component-name. IF sy-tabix < lines( lt_components ). rv_csv = rv_csv && iv_separator. ENDIF. ENDLOOP. rv_csv = rv_csv && gc_newline.
" Datenzeilen DATA lv_value TYPE string.
LOOP AT it_data ASSIGNING FIELD-SYMBOL(<ls_row>). LOOP AT lt_components INTO ls_component. ASSIGN COMPONENT ls_component-name OF STRUCTURE <ls_row> TO FIELD-SYMBOL(<lv_field>). IF sy-subrc = 0. lv_value = <lv_field>.
" Escape fuer CSV (Anführungszeichen verdoppeln, bei Trennzeichen quotieren) IF lv_value CS iv_separator OR lv_value CS '"' OR lv_value CS gc_newline. REPLACE ALL OCCURRENCES OF '"' IN lv_value WITH '""'. lv_value = |"{ lv_value }"|. ENDIF.
rv_csv = rv_csv && lv_value. ENDIF.
IF sy-tabix < lines( lt_components ). rv_csv = rv_csv && iv_separator. ENDIF. ENDLOOP. rv_csv = rv_csv && gc_newline. ENDLOOP. ENDMETHOD.
METHOD export_to_csv_binary. DATA(lv_csv) = export_to_csv( it_data = it_data iv_separator = iv_separator ).
" BOM fuer UTF-8 (Excel-Erkennung) DATA(lv_bom) = CONV xstring( 'EFBBBF' ).
" Konvertieren rv_binary = lv_bom && cl_abap_conv_codepage=>create_out( codepage = iv_encoding )->convert( lv_csv ). ENDMETHOD.
ENDCLASS.OData-Export fuer Excel
Fiori Apps koennen Daten direkt ueber OData als Excel exportieren:
-- CDS View mit Excel-Export-Annotation@EndUserText.label: 'Sales Orders for Excel Export'@OData.publish: true@UI.headerInfo: { typeName: 'Auftrag', typeNamePlural: 'Auftraege'}define view entity ZC_SalesOrderExcel as projection on ZI_SalesOrder{ @UI.lineItem: [{ position: 10, label: 'Auftragsnummer' }] key OrderID,
@UI.lineItem: [{ position: 20, label: 'Kunde' }] Customer,
@UI.lineItem: [{ position: 30, label: 'Betrag' }] @Semantics.amount.currencyCode: 'Currency' Amount,
@UI.lineItem: [{ position: 40, label: 'Waehrung' }] @Semantics.currencyCode: true Currency,
@UI.lineItem: [{ position: 50, label: 'Bestelldatum' }] OrderDate,
@UI.lineItem: [{ position: 60, label: 'Status' }] Status}Fiori Elements bietet dann automatisch einen Export-Button fuer Excel.
Fehlerbehandlung
Robuster Import mit Fehlerprotokoll
" Excel-Import mit detailliertem FehlerprotokollCLASS zcl_excel_import_validator DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_import_error, row TYPE i, column TYPE string, value TYPE string, error_type TYPE string, message TYPE string, END OF ty_import_error, ty_import_errors TYPE STANDARD TABLE OF ty_import_error WITH KEY row column.
TYPES: BEGIN OF ty_import_result, success TYPE abap_bool, rows_total TYPE i, rows_success TYPE i, rows_error TYPE i, errors TYPE ty_import_errors, data TYPE REF TO data, END OF ty_import_result.
METHODS: import_and_validate IMPORTING iv_xlsx TYPE xstring ir_target_type TYPE REF TO cl_abap_tabledescr RETURNING VALUE(rs_result) TYPE ty_import_result RAISING cx_xco_runtime_exception.
ENDCLASS.
CLASS zcl_excel_import_validator IMPLEMENTATION.
METHOD import_and_validate. " Ziel-Tabelle erstellen CREATE DATA rs_result-data TYPE HANDLE ir_target_type. ASSIGN rs_result-data->* TO FIELD-SYMBOL(<lt_data>).
" Excel parsen DATA(lo_document) = xco_cp_xlsx=>document->for_file_content( iv_xlsx ). DATA(lo_worksheet) = lo_document->spreadsheet->worksheet->at_position( 1 ). DATA(lo_used_area) = lo_worksheet->used_area( ).
DATA(lv_first_row) = lo_used_area->row_selection->first_row->position->value. DATA(lv_last_row) = lo_used_area->row_selection->last_row->position->value.
rs_result-rows_total = lv_last_row - lv_first_row. " Ohne Header rs_result-success = abap_true.
" Struktur der Zieltabelle DATA(lo_struct_descr) = CAST cl_abap_structdescr( ir_target_type->get_table_line_type( ) ). DATA(lt_components) = lo_struct_descr->get_components( ).
" Zeilen verarbeiten (ab Zeile 2 = nach Header) DO ( lv_last_row - lv_first_row ) TIMES. DATA(lv_row) = lv_first_row + sy-index.
" Neue Zeile erstellen DATA ls_line TYPE REF TO data. CREATE DATA ls_line TYPE HANDLE lo_struct_descr. ASSIGN ls_line->* TO FIELD-SYMBOL(<ls_line>).
DATA(lv_row_has_error) = abap_false.
" Spalten durchlaufen LOOP AT lt_components INTO DATA(ls_component). DATA(lv_col) = sy-tabix.
DATA(lo_cursor) = lo_worksheet->cursor( io_column = xco_cp_xlsx=>coordinate->for_numeric_value( lv_col ) io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row ) ).
DATA lv_value TYPE string. lo_cursor->get_cell( )->value->read_to( REF #( lv_value ) ).
" Wert zuweisen mit Fehlerbehandlung ASSIGN COMPONENT ls_component-name OF STRUCTURE <ls_line> TO FIELD-SYMBOL(<lv_field>).
IF sy-subrc = 0. TRY. " Typkonvertierung versuchen <lv_field> = lv_value.
CATCH cx_sy_conversion_no_number cx_sy_conversion_overflow cx_sy_conversion_error INTO DATA(lx_conv). " Konvertierungsfehler protokollieren APPEND VALUE #( row = lv_row column = ls_component-name value = lv_value error_type = 'CONVERSION' message = lx_conv->get_text( ) ) TO rs_result-errors. lv_row_has_error = abap_true. rs_result-success = abap_false. ENDTRY. ENDIF. ENDLOOP.
" Zeile zur Ergebnistabelle hinzufuegen (auch mit Fehlern) IF lv_row_has_error = abap_false. INSERT <ls_line> INTO TABLE <lt_data>. rs_result-rows_success = rs_result-rows_success + 1. ELSE. rs_result-rows_error = rs_result-rows_error + 1. ENDIF. ENDDO. ENDMETHOD.
ENDCLASS.Best Practices
Empfehlungen fuer Excel-Verarbeitung
| Aspekt | Empfehlung |
|---|---|
| Kleine Datenmengen | XCO Spreadsheet API direkt |
| Grosse Datenmengen | Streaming/Chunking oder CSV |
| Komplexe Formatierung | Externe XLSX-Bibliothek |
| Maximale Kompatibilitaet | CSV mit UTF-8 BOM |
| Fiori-Integration | OData-Export nutzen |
Performance-Tipps
- Batch-Verarbeitung: Grosse Dateien in Chunks verarbeiten
- Streaming: Bei sehr grossen Dateien Stream-basierte Verarbeitung
- Caching: Wiederholte Exporte cachen
- Asynchron: Grosse Exporte im Hintergrund (bgPF)
Sicherheits-Tipps
- Dateityp validieren: MIME-Type pruefen vor Verarbeitung
- Groessenlimit: Maximale Dateigroesse definieren
- Inhalt validieren: Daten vor Import validieren
- Berechtigungen: Export/Import-Berechtigung pruefen
Zusammenfassung
Excel-Verarbeitung in ABAP Cloud nutzt moderne APIs:
- XCO Spreadsheet API fuer Standard-Export/Import
- Generische Exporter fuer beliebige Tabellentypen
- Formatierung ueber Zellenstile und Eigenschaften
- Multiple Worksheets fuer komplexe Reports
- RAP Actions fuer nahtlose Fiori-Integration
- CSV als Alternative fuer maximale Kompatibilitaet
Die Wahl der richtigen Methode haengt von deinen Anforderungen ab: Datenvolumen, Formatierungskomplexitaet und Zielgruppe.
Verwandte Themen
- RAP Actions und Functions - Actions fuer Download/Upload
- File Operations - Dateiverarbeitung in ABAP
- Attachment Handling - Dokumentenanhänge in RAP