ABAP – How to create a file in the application server

Summary

If you need a simple way to generate some custom files this code might help you. I developed this to be able to download very fast very large internal tables from the application server to the local PC. This project started because I needed to download a lot of records (millions) in an Access and/or Excel file. The usual way (direct download to Access or Excel) failed because it was taking a lot of time, so I downloaded the tables as CSV files on the computer and another script (in VBA) imported them into the Access or Excel files. After testing this I managed to reduce the download time from 20 minutes (or more) to 30 seconds for the download plus another 10 seconds to import in the target file. I tested this with the MARA table.

In the example below I’ll show you how to create a generic file on the application server. This code was later used to generate a CSV file and download it on the local PC. This class features a browse and download function for the file and other helpful methods.

The code

The file represents an instance of the class ZCL_RAW_FILE. Inside the class we have the property GV_FILE_CONTENT that holds the contents of the file (table with header).

The class also has a property to hold the file’s name GV_FILE_NAME and the file’s extension GV_FILE_EXTENSION. Those two will be concatenated to generate the full name of the file when exported.

Other helpfull methods are: SAVE_TO_PATH to start the GUI Download, BROWSE_FOR_SAVE_PATH to browse for the path to download to, SET_FILE_EXTENSION and SET_FILE_NAME to change the file’ extension and name (the”.” will be automatically put in place), GET_FILE_SIZE to query the current size of the file, the check_path method to help you validate a path in case you ask the user to enter it manually.

types: tyt_raw_string TYPE STANDARD TABLE OF string WITH DEFAULT KEY .
...
private section.
  data GV_FILE_EXTENSION type STRING .
  data GV_FILE_SIZE type INT4 .
  data GV_FILE_CONTENT type TYT_RAW_STRING .
  data GV_FILE_NAME type STRING .

There is also a property that holds the file’s size GV_FILE_SIZE. This is used when we need to know the current size of the file before exporting it to the local computer or before appending to it.

class ZCL_RAW_FILE

Here we have the code as it appears in the source code view. You can just copy it and the system will take care of it automatically.

class ZCL_RAW_FILE definition
  public
  create public .
 
public section.
 
  class-methods BROWSE_FOR_SAVE_PATH
    returning
      value(MV_PATH) type STRING .
  methods SET_FILE_EXTENSION
    importing
      !MV_FILE_EXTENSION type STRING .
  methods CLEAR_CONTENTS .
  methods GET_FILE_SIZE
    returning
      value(MV_FILE_SIZE) type INT4 .
  methods SET_FILE_NAME
    importing
      !MV_FILE_NAME type STRING .
  methods CONSTRUCTOR
    importing
      !MV_ENCODING type STRING default SPACE
      !MV_FILE_NAME type STRING
      !MV_FILE_EXTENSION type STRING .
  methods SAVE_TO_PATH
    importing
      value(MV_SAVE_PATH) type STRING .
  methods ADD_LINE
    importing
      !MV_STRING_LINE type STRING .
  class-methods CHECK_PATH
    changing
      !MV_PATH_TO_CHECK type STRING
    returning
      value(MV_IS_CORRECT) type ABAP_BOOL .
protected section.
 
  types:
    tyt_raw_string TYPE STANDARD TABLE OF string WITH DEFAULT KEY .
 
  data GV_FILE_ENCODING type ABAP_ENCOD value SPACE ##NO_TEXT.
private section.
 
  data GV_FILE_EXTENSION type STRING .
  data GV_FILE_SIZE type INT4 .
  data GV_FILE_CONTENT type TYT_RAW_STRING .
  data GV_FILE_NAME type STRING .
ENDCLASS.
 
 
 
CLASS ZCL_RAW_FILE IMPLEMENTATION.
 
 
* ---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_RAW_FILE->ADD_LINE
* +-------------------------------------------------------------------------------------------------+
* | [--->] MV_STRING_LINE                 TYPE        STRING
* +--------------------------------------------------------------------------------------
  METHOD ADD_LINE.
    " add the length of the new row to the file length plus the end of line terminators \n\r
    me->gv_file_size = me->gv_file_size + strlen( mv_string_line ) + 2.
 
    " append the contents to the file contents
    APPEND mv_string_line TO gv_file_content.
  ENDMETHOD.
 
 
* ---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_RAW_FILE=>BROWSE_FOR_SAVE_PATH
* +-------------------------------------------------------------------------------------------------+
* | [<-()] MV_PATH                        TYPE        STRING
* +--------------------------------------------------------------------------------------
  METHOD browse_for_save_path.
    " call the GUI browse for folder dialog
    cl_gui_frontend_services=>directory_browse(
      EXPORTING
        window_title         = 'Please select the folder to save to'
        initial_folder       = 'c:\'
      CHANGING
        selected_folder      = mv_path " here we get the selected path
      EXCEPTIONS
        cntl_error           = 1
        error_no_gui         = 2
        not_supported_by_gui = 3
        OTHERS               = 4 ).
 
    " if an error occured
    IF sy-subrc <> 0.
      " Implement suitable error handling here
      MESSAGE 'Folder selection failed' TYPE 'E'.
    ENDIF.
  ENDMETHOD.
 
 
* ---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_RAW_FILE=>CHECK_PATH
* +-------------------------------------------------------------------------------------------------+
* | [<-->] MV_PATH_TO_CHECK               TYPE        STRING
* | [<-()] MV_IS_CORRECT                  TYPE        ABAP_BOOL
* +--------------------------------------------------------------------------------------
  METHOD check_path.
    DATA: lo_regex   TYPE REF TO cl_abap_regex,
          lo_matcher TYPE REF TO cl_abap_matcher.
    DATA: pos TYPE i,
          chr TYPE char01.
 
*   get the last character in string the path should look like: x:\zxy\...\
    pos = strlen( mv_path_to_check ) - 1.
    chr = mv_path_to_check+pos.
 
* Concatenates '\' at the end if the path does not end with it
    IF chr <> '\'.
      CONCATENATE mv_path_to_check '\' INTO mv_path_to_check.
    ENDIF.
 
* Establishes the REGEX to check if the rest of the path is correct
    CREATE OBJECT lo_regex
      EXPORTING
        pattern     = '^[a-zA-Z]:\\.*\\'
        ignore_case = abap_true.
 
* Establishes the path to be checked
    CREATE OBJECT lo_matcher
      EXPORTING
        regex = lo_regex
        text  = mv_path_to_check.
 
* Confirms the path validity
    lo_matcher->match(
      RECEIVING
        success = mv_is_correct ).
  ENDMETHOD.
 
 
* ---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_RAW_FILE->CLEAR_CONTENTS
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------
  METHOD clear_contents.
    " clear the contents of the internal table
    CLEAR gv_file_content.
 
    " set the file size to zero
    gv_file_size = 0.
  ENDMETHOD.
 
 
* ---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_RAW_FILE->CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* | [--->] MV_ENCODING                    TYPE        STRING (default =SPACE)
* | [--->] MV_FILE_NAME                   TYPE        STRING
* | [--->] MV_FILE_EXTENSION              TYPE        STRING
* +--------------------------------------------------------------------------------------
  METHOD constructor.
    " initialize the size of the current file
    me->gv_file_size = 0.
 
    " set the file encoding
    me->gv_file_encoding = mv_encoding.
 
    " set the file's name
    me->set_file_name(
      EXPORTING
        mv_file_name = mv_file_name ).
 
    "  and extension
    me->set_file_extension(
      EXPORTING
        mv_file_extension = mv_file_extension ).
  ENDMETHOD.
 
 
* ---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_RAW_FILE->GET_FILE_SIZE
* +-------------------------------------------------------------------------------------------------+
* | [<-()] MV_FILE_SIZE                   TYPE        INT4
* +--------------------------------------------------------------------------------------
  METHOD GET_FILE_SIZE.
    " return the current file's size
    mv_file_size = me->gv_file_size.
  ENDMETHOD.
 
 
* ---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_RAW_FILE->SAVE_TO_PATH
* +-------------------------------------------------------------------------------------------------+
* | [--->] MV_SAVE_PATH                   TYPE        STRING
* +--------------------------------------------------------------------------------------
  METHOD save_to_path.
    DATA: lv_complete_file_name TYPE string,
          lv_path_is_correct    TYPE abap_bool VALUE abap_true.
 
    " check if the given path is correct
    zcl_raw_file=>check_path(
      CHANGING
        mv_path_to_check = mv_save_path
      RECEIVING
        mv_is_correct    = lv_path_is_correct ).
 
    IF lv_path_is_correct = abap_true.
      IF gv_file_extension IS NOT INITIAL.
        " create the complete path + file name + extension
        CONCATENATE mv_save_path gv_file_name gv_file_extension INTO lv_complete_file_name.
      ELSE.
        " create the complete path + file name + extension
        CONCATENATE mv_save_path gv_file_name INTO lv_complete_file_name.
      ENDIF.
 
      " call GUI download function
      CALL FUNCTION 'GUI_DOWNLOAD'
        EXPORTING
          filename = lv_complete_file_name
          codepage = me->gv_file_encoding
        TABLES
          data_tab = me->gv_file_content.
 
      " if errors occured display a message
      IF sy-subrc <> 0.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      ENDIF.
    ELSE.
      " abort the save process if an incorrect path is found
      MESSAGE 'Incorrect path, aborting export.' TYPE 'E'.
    ENDIF.
  ENDMETHOD.
 
 
* ---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_RAW_FILE->SET_FILE_EXTENSION
* +-------------------------------------------------------------------------------------------------+
* | [--->] MV_FILE_EXTENSION              TYPE        STRING
* +--------------------------------------------------------------------------------------
  method SET_FILE_EXTENSION.
    " set the current file's extension
    me->gv_file_extension = mv_file_extension.
  endmethod.
 
 
* ---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_RAW_FILE->SET_FILE_NAME
* +-------------------------------------------------------------------------------------------------+
* | [--->] MV_FILE_NAME                   TYPE        STRING
* +--------------------------------------------------------------------------------------
  method SET_FILE_NAME.
    " set the current file's name
    me->gv_file_name = mv_file_name.
  endmethod.
ENDCLASS.

Example of use

After building the class you can use it as follows:

" variables related to the file to which we export
    DATA: lo_my_file TYPE REF TO zcl_csv_file, " file that is generated
          lv_current_file_length TYPE i, " the current length of the current file
          lv_path_to_save TYPE string. " the path where you save the file
* create the file object
    CREATE OBJECT lo_my_file
      EXPORTING
        mv_file_name = 'my_file'
        mv_file_extension = 'txt'
      .
* You can use the static method to browse for a path on the local host
    lv_path_to_save = zcl_csv_file=>browse_for_save_path
* Write the first line in the first file
    lo_my_file->add_line( 'some text on the first line' ).
* get the file size
    lv_current_file_length = lo_my_file->get_file_size( ).
* then save the current data to the file
    lo_my_file->save_to_path( lv_path_to_save ).
* clear the contents
    lo_my_file->clear_contents( ).
* change the file name
    lo_my_file->set_file_name( 'my_second_file' ).
* add some other line to the file
    lo_my_file->add_line( 'second line in the file' ).
* save it again with the new contents
    lo_my_file->save_to_path( 'c:\folder\ ' ).