I had to write a few SAP ABAP programs which required the uploading of a CSV file in order to import data into SAP. Instead of writing and then copying a FORM into multiple programs (or calling the FORM from another program, or creating a function module, etc.) I developed an ABAP class that would handle the file upload and conversion of the CSV into an internal table. Below is the code for my class, the first part is automatically generated from the data entered in the class edit screen. You can tell the automatically generated code because it is formatted the opposite of code which has been formatted with the "pretty printer," the reserved words are in lower case and the variables are in upper case.

class Z_BC_USER_FILEIO definition
 public
 final
 create public .

public section.

 type-pools ABAP .
 methods UPLOAD
  importing
   !I_SHOW_DIALOG type ABAP_BOOL default ' '
   !I_WINDOW_TITLE type STRING default ''
   !I_INITIAL_DIRECTORY type STRING default ''
   !I_DEFAULT_FILE type STRING default '*.*'
   !I_DEFAULT_EXTENSION type STRING default '*.*'
   !I_FILE_FILTER type STRING default '*.*'
   !I_UPLOAD_PATH type STRING default ''
   !I_CSV_PARSE type ABAP_BOOL default ' '
   !I_CSV_DATA_SEP type CHAR1 default ','
   !I_CSV_ESC_CHAR type CHAR1 default '"'
   !I_CSV_NUM_COLS type I default 9999
   !I_CSV_SKIPHEAD type ABAP_BOOL default ' '
  exporting
   !E_FILE_RAWDATA type TEXTLINE_T
  changing
   !E_FILE_CSV_STRUCT type ANY optional
   !E_FILE_CSV_TABLE type STANDARD TABLE optional
  exceptions
   UPLOAD_ERROR
   PARSE_ERROR
   PATH_ERROR
   PROGRAM_ERROR .

private section.

 data P_UPLOAD_PATH type STRING .

 type-pools ABAP .
 class-methods PARSE_CSV
  importing
   !I_CSV_DATA_SEP type CHAR1 default ','
   !I_CSV_ESC_CHAR type CHAR1 default '"'
   !I_CSV_NUM_COLS type I default 9999
   !I_CSV_SKIPHEAD type ABAP_BOOL default ' '
   !I_FILE_RAWDATA type TEXTLINE_T
  exporting
   !E_FILE_CSV_STRUCT type ANY
   !E_FILE_CSV_TABLE type STANDARD TABLE
  exceptions
   PARSE_ERROR
   TABLE_ERROR .


METHOD upload.
 DATA: lf_error TYPE i VALUE 0,
    lf_upload_path TYPE char1024,
    lf_window_title TYPE string.

 p_upload_path = i_upload_path.
 lf_upload_path = p_upload_path.

 " let user select file if needed
 IF i_show_dialog = 'X'.
  IF I_WINDOW_TITLE IS INITIAL.
   lf_window_title = text-002.
  ELSE.
   lf_window_title = I_WINDOW_TITLE.
  ENDIF.

  CALL FUNCTION '/SAPSLL/BROWSE_LOCAL_FILE_SYST'
   EXPORTING
    iv_window_title = lf_window_title
    iv_default_extension = i_default_extension
    iv_default_filename = i_default_file
    iv_file_filter = i_file_filter
    iv_initial_directory = i_initial_directory
    iv_multiselection = ' '
   IMPORTING
    ev_rcode = lf_error
   CHANGING
    iv_path = lf_upload_path
    .
 ELSE.
  " Must have the path if not using the dialog
  IF p_upload_path IS INITIAL.
   lf_error = 1.
  ENDIF.
 ENDIF.

 " upload the file if we have a path
 IF lf_error = 0.
  CALL METHOD cl_gui_frontend_services =>gui_upload
   EXPORTING
    filename = p_upload_path
   CHANGING
    data_tab = e_file_rawdata
   EXCEPTIONS
    file_open_error = 1
    file_read_error = 2
    no_batch = 3
    gui_refuse_filetransfer = 4
    invalid_type = 5
    no_authority = 6
    unknown_error = 7
    bad_data_format = 8
    header_not_allowed = 9
    separator_not_allowed = 10
    header_too_long = 11
    unknown_dp_error = 12
    access_denied = 13
    dp_out_of_memory = 14
    disk_full = 15
    dp_timeout = 16
    not_supported_by_gui = 17
    error_no_gui = 18
    others = 19
      .
  IF sy-subrc = 0.
   IF i_csv_parse = 'X'.
    "IF ( e_file_csv_struct IS NOT INITIAL ) AND ( e_file_csv_table IS NOT INITIAL ).
    CALL METHOD parse_csv
     EXPORTING
      i_csv_data_sep = i_csv_data_sep
      i_csv_esc_char = i_csv_esc_char
      i_file_rawdata = e_file_rawdata
      i_csv_num_cols = i_csv_num_cols
      i_csv_skiphead = i_csv_skiphead
     IMPORTING
      e_file_csv_struct = e_file_csv_struct
      e_file_csv_table = e_file_csv_table
     EXCEPTIONS
      parse_error = 1
      table_error = 2
      OTHERS = 3
      .
    IF sy-subrc <> 0.
     MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
           WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
           RAISING parse_error.
    ENDIF.

    "ELSE. " I_CSV_PARSE set without other CSV parameters
    " MESSAGE s000(Z_gts) DISPLAY LIKE 'E'
    "     WITH text-001 space space space
    "     RAISING program_error.
    "ENDIF.
   ENDIF.

  ELSE. " GUI_UPLOAD error
   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
         WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
         RAISING upload_error.
  ENDIF.

 ELSE. " Path not set or error with user dialog
  MESSAGE s000(Z_gts) DISPLAY LIKE 'E'
      WITH text-000 p_upload_path space space
      RAISING path_error.
 ENDIF.
ENDMETHOD.


METHOD parse_csv.
 DATA: lt_csv_table_struct TYPE REF TO data,
    lt_csv_table_desc TYPE REF TO cl_abap_tabledescr,
    ls_csv_table_desc TYPE REF TO cl_abap_structdescr,
    lt_csv_table_flds TYPE abap_compdescr_tab,
    ls_csv_table_flds TYPE abap_compdescr,
    ls_file_rawline TYPE LINE OF textline_t,
    lt_file_columns TYPE table_of_strings,
    ls_file_columns TYPE LINE OF table_of_strings,
    lf_skip_line  TYPE abap_bool VALUE ' ',
    ls_fieldcatalog TYPE lvc_s_fcat,
    lt_fieldcatalog TYPE lvc_t_fcat,
    lt_dynamictable TYPE REF TO data,
    ls_dynamicline TYPE REF TO data,
    lf_loop_count  TYPE i,
    lf_line_count  TYPE i,
    lf_fieldname  TYPE char30.

 FIELD-SYMBOLS: <fs_csv_table_struct> TYPE STANDARD TABLE,
         <fs_dynamic_table> TYPE STANDARD TABLE,
         <fs_dynamic_lines> TYPE ANY,
         <fs_dynamic_field> TYPE ANY.

 " determine the structure of the destination table
 GET REFERENCE OF e_file_csv_table INTO lt_csv_table_struct.
 ASSIGN lt_csv_table_struct->* TO <fs_csv_table_struct>.
 lt_csv_table_desc ? =
  cl_abap_structdescr =>describe_by_data_ref( lt_csv_table_struct ).
 ls_csv_table_desc ? = lt_csv_table_desc->get_table_line_type( ).
 lt_csv_table_flds = ls_csv_table_desc->components.

 " use destination table structure for results table
 LOOP AT lt_csv_table_flds INTO ls_csv_table_flds.
  ls_fieldcatalog-fieldname = ls_csv_table_flds-name.
  ls_fieldcatalog-inttype = ls_csv_table_flds-type_kind.
  ls_fieldcatalog-intlen = ls_csv_table_flds-length.
  ls_fieldcatalog-decimals = ls_csv_table_flds-decimals.
  APPEND ls_fieldcatalog TO lt_fieldcatalog.
 ENDLOOP.

 " build a dynamic table to put the CSV contents into
 CALL METHOD cl_alv_table_create =>create_dynamic_table
  EXPORTING
   it_fieldcatalog = lt_fieldcatalog
  IMPORTING
   ep_table = lt_dynamictable
  EXCEPTIONS
   generate_subpool_dir_full = 1
   OTHERS = 2.
 IF sy-subrc <> 0.
  MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
        RAISING table_error.
 ENDIF.

 " finish dynamic table, create dynamic table lines
 ASSIGN lt_dynamictable->* TO <fs_dynamic_table>.
 CREATE DATA ls_dynamicline LIKE LINE OF <fs_dynamic_table>.
 ASSIGN ls_dynamicline->* TO <fs_dynamic_lines>.

 " delete header line if needed
 IF i_csv_skiphead = 'X'.
  lf_skip_line = 'X'.
 ENDIF.

 " process the CSV file raw data
 LOOP AT i_file_rawdata INTO ls_file_rawline.
  CALL FUNCTION 'RSDS_CONVERT_CSV'
   EXPORTING
    i_data_sep = i_csv_data_sep
    i_esc_char = i_csv_esc_char
    i_record = ls_file_rawline
    i_field_count = i_csv_num_cols
   IMPORTING
    e_t_data = lt_file_columns
   EXCEPTIONS
    escape_no_close = 1
    escape_improper = 2
    conversion_error = 3
    OTHERS = 4.
  IF sy-subrc <> 0. " RSDS_CONVERT_CSV error
   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
         WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
         RAISING parse_error.
  ENDIF.

  " store CSV fields into dynamic table
  lf_loop_count = 1.
  DESCRIBE TABLE lt_file_columns LINES lf_line_count.
  LOOP AT lt_csv_table_flds INTO ls_csv_table_flds.
   MOVE ls_csv_table_flds-name TO lf_fieldname.
   ASSIGN COMPONENT lf_fieldname
    OF STRUCTURE <fs_dynamic_lines>
TO <fs_dynamic_field>.
   READ TABLE lt_file_columns INDEX lf_loop_count INTO ls_file_columns.

   IF lf_loop_count < = lf_line_count.
    <fs_dynamic_field> = ls_file_columns.
   ELSE.
    <fs_dynamic_field> = ''.
   ENDIF.

   lf_loop_count = lf_loop_count + 1.
  ENDLOOP.

  " move dynamic table line to resultant table, unless we are skipping
  IF lf_skip_line = ' '.
   MOVE-CORRESPONDING <fs_dynamic_lines> TO e_file_csv_struct.
   APPEND e_file_csv_struct TO e_file_csv_table.
  ELSE.
   lf_skip_line = ' '.
  ENDIF.
 ENDLOOP.
ENDMETHOD.

Comments (newest first)

Anonymous
Is there a limit of lines that can be imported? I heard 999
Steve Oldner
Hmmmm, glad I d not have to re-invent much. Always looking for a better way to read in CSVs with all the varieties they have.

Thanks!

Tags: Blogger Object-oriented SAP Code ABAP

Published: 2011-07-07