ZDOC_2715

Два способа выгрузить файл

Без предисловий, сразу код в начальной итерации:

DATA: lv_template TYPE string VALUE 'ZR_CA001.DOCX'.
DATA: lv_file TYPE xstring.
DATA: lt_data TYPE STANDARD TABLE OF tbl1024.
DATA: lv_len TYPE i.

zcl_text_tools=>read_mimeobject( EXPORTING iv_mimeobject = conv #( lv_template )
                                 IMPORTING ev_bin = lv_file ).
* ... some magic happens here ...

lv_len = xstrlen( lv_file ).

CALL METHOD cl_bcs_convert=>xstring_to_xtab
  EXPORTING
    iv_xstring = lv_file
  IMPORTING
    et_xtab    = lt_data[].

cl_gui_frontend_services=>show_document( EXPORTING document_name = lv_template
                                                   mime_type     = ''
                                                   data_length   = lv_len
                                          CHANGING document_data = lt_data[]
                                        EXCEPTIONS OTHERS = 1 ).

А теперь можно его покритиковать.

Во-первых, имя файла.

Пользователю должно быть удобно и понятно.

Техническое имя – непонятно, значит от него надо избавиться. В некоторых подходах можно встретить запрос имени файла (и пути к нему заодно):

lc_def_fname = 'ZR_CA001'.
CALL METHOD cl_gui_frontend_services=>file_save_dialog
  EXPORTING
    file_filter               = zcl_excel_common=>c_xlsx_file_filter
    default_file_name         = lc_def_fname
    prompt_on_overwrite       = 'X'
  CHANGING
    filename                  = lv_filename
    path                      = lv_path
    fullpath                  = lv_fullpath
    user_action               = lv_user_action
  EXCEPTIONS
    cntl_error                = 1
    error_no_gui              = 2
    not_supported_by_gui      = 3
    invalid_default_file_name = 4
    OTHERS                    = 5.

CHECK lv_user_action NE cl_gui_frontend_services=>action_cancel.

Это не всегда удобно. Спрашивать – это дополнительное движение. Оно критично, если пользователю надо проконтролировать место хранения файла. В моём типичном случае файл всегда выгружается во временную папку, пользователь контролирует его содержимое, отправляет на печать, закрывает и забывает. Поэтому место хранения контролировать не надо.

А имя файла можно задать понятное до деталей:

lv_filename = |Order AA { ls_order-ordnu } { ls_order-orddt DATE = USER } { ls_order-ename }|.

Это совсем не сложно, не стоит этого бояться.

Во-вторых, полномочия на стороне пользователя.

Тыкать в дополнительные нудные предупреждения – неудобно.

В моём случае show_document встречает две проверки:

А) Проверка на выгрузку файла в папку:

Word security 1

Б) Проверка на открытие файла:

Word security 2

Первую проверку можно и “запомнить” и обосновать по корпоративной политике.

А вот вторую проверку “запомнить” нельзя. Варианта два – копать или обходить. Нормальные герои всегда идут в обход.

Если метод cl_gui_frontend_services=>show_document заставляет SAP GUI искать программу, ассоциированную с данным типом файлов, то метод cl_gui_frontend_services=>execute работает хитрее. Он просто предоставляет операционной системе самой разобраться что к чему.

Вот такой маленький proof-of-concept:

DATA: lv_template TYPE string VALUE 'ZR_CA001.DOCX'.
DATA: lv_file TYPE xstring.

zcl_text_tools=>read_mimeobject( EXPORTING iv_mimeobject = CONV #( lv_template )
                                 IMPORTING ev_bin = lv_file ).
* ... some magic happens here ...

DATA: lv_filename TYPE string.

lv_filename = 'Order AA 123 15.06.2018 Ivanov Ivan'.

zcl_file=>show(
  EXPORTING
    iv_file     = lv_file
    iv_filename = lv_filename
    iv_ext      = '.docx'
).

Метод SHOW реализован отдельно:

METHOD show.

*IV_FILENAME = Order AA 123 15.06.2018 Ivanov Ivan
*IV_EXT      = .docx

*[TODO] если WDA = использовать другой метод
*[TODO] если ITS = использовать другой метод

  DATA: lv_filename TYPE string.

  lv_filename = iv_filename.
  CONDENSE lv_filename.

  DATA: lt_data TYPE STANDARD TABLE OF tbl1024.

  DATA: lv_len TYPE i.

  lv_len = xstrlen( iv_file ).

  CALL METHOD cl_bcs_convert=>xstring_to_xtab
    EXPORTING
      iv_xstring = iv_file
    IMPORTING
      et_xtab    = lt_data[].

  DATA: lv_temp_dir TYPE string.

  cl_gui_frontend_services=>get_temp_directory(
    CHANGING
      temp_dir             = lv_temp_dir "D:\Users\<username>\AppData\Local\SAP\SAP GUI\tmp
    EXCEPTIONS
      cntl_error           = 1
      error_no_gui         = 2
      not_supported_by_gui = 3
      OTHERS               = 4 ).
  IF sy-subrc <> 0.
*   [TODO] обработка ошибок
*   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
*              WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

  CALL METHOD cl_gui_cfw=>flush
    EXCEPTIONS
      OTHERS = 0.

  DATA: lv_fullname TYPE string.

  DATA: lv_try TYPE i value 1.

  "D:\Users\<username>\AppData\Local\SAP\SAP GUI\tmp\Order AA 123 15.06.2018 Ivanov Ivan.docx
  lv_fullname = |{ lv_temp_dir }\\{ lv_filename }{ iv_ext }|.

  DO.

    cl_gui_frontend_services=>gui_download(
      EXPORTING
        bin_filesize              = lv_len
        filename                  = lv_fullname
        filetype                  = 'BIN'
        no_auth_check             = 'X'
      CHANGING
        data_tab                  = lt_data[]
      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.
      cl_gui_frontend_services=>execute( EXPORTING document = lv_fullname ).
      EXIT.
    ELSEIF sy-subrc = 15.
      lv_try = lv_try + 1.
      "access_denied  = файл уже открыт в приложении, дописываем счетчик пока не достигнем успеха
      "D:\Users\<username>\AppData\Local\SAP\SAP GUI\tmp\Order AA 123 15.06.2018 Ivanov Ivan (2).docx
      lv_fullname = |{ lv_temp_dir }\\{ lv_filename } ({ lv_try }){ iv_ext }|.

      IF lv_try > 100.
        "[TODO] Panic!
        EXIT.
      ENDIF.

    ELSE.
      EXIT.
      "[TODO] Other error!
    ENDIF.

  ENDDO.

ENDMETHOD.

Профит.

PS. И это не конец истории. Не забыть бы про мусор во временной папке, а то мало ли что.

Есть варианты:

  • C:\Users\<username>\AppData\Local\Temp – системная временная папка; не common way
  • C:\Users\<username>\AppData\Local\SAP\SAP GUI\tmp\ – временная папка самого SAP GUI; my way
  • C:\Users\<username>\Documents\SAP\SAP GUI – папка по-умолчанию для работы с документами; не в этот раз
  • [включите воображение]

Метод SHOW_DOCUMENT выгружает файл во вторую папку и там есть  параметр KEEP_FILE. Очевидно тогда, что если флаг специально не выставить, то файл будет подчищен автоматически; вопрос только в какой момент.

В нашем новом методе EXECUTE данный параметр отсутствует. Что это значит? Интересно, чистит ли папку кто-нибудь и когда-нибудь?

Документация говорит:

Documents in the temporary directory will be deleted, when SAP GUI is closed. You can configure the deletion of files also in other directories. For information on this, refer to the security guide.

Это успокаивает, значит хорошо что мы выгружаем именно в эту папку.

Мусор во временной папке

А к третьей папке есть настройка:

Lifetime