SAP ABAP 上传下载文件到文件服务器

Posted 想发财的小夏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SAP ABAP 上传下载文件到文件服务器相关的知识,希望对你有一定的参考价值。

需求:项目上要和外围系统做接口,把传过来的文件写入到sap的文件服务器或者存入表中,让外围系统过来存取,但是为了减少SAP应用服务器的负担,需要一台独立的文件服务器共享目录到SAP应用服务器,实现往SAP应用服务器上写文件,实际上是写在了这台独立的文件服务器上。我们需要让Basis把文件服务器和SAP应用服务器做NFS,开放给SAP相应权限。

AL11可以进入SAP应用服务器查看文件服务器的目录,SAP中可以在该目录上新建文件夹,也就是在该目录里新建目录。做好NFS后,SAP上用AL11进去点击配置后进入该界面,点击新建/保存,建立相对应目录,如下图
在这里插入图片描述

<本文是使用的是Http的Restful方式制作的接口>
因为涉及到存取,所以我们这里分为Get 和 Post。
在这里插入图片描述

下载文件

下载文件分为1. PDF直接存系统表2. 读取AL11的文件服务器 两种

  DATA:
    lv_uuid    TYPE string,
    lv_insplot TYPE string,
    lv_regex   TYPE string.

* 请求参数
  DATA(lv_query) = server->request->get_header_field( '~query_string' ).
  IF lv_query IS INITIAL.
    server->response->set_status( code = 404 reason = '文件请求参数未指定' ).
    RETURN.
  ENDIF.
  TRANSLATE lv_query TO LOWER CASE.

* 存入AL11文件服务器
  CLEAR lv_uuid.
  lv_regex = '^uuid=(.*)'.
  FIND REGEX lv_regex IN lv_query SUBMATCHES lv_uuid.
  IF sy-subrc = 0.
* 去掉 & 符号
    REPLACE FIRST OCCURRENCE OF '&' IN lv_uuid WITH space.
    IF lv_uuid IS INITIAL.
      server->response->set_status( code = 404 reason = '文件唯一标识未指定' ).
    ELSE.
      CALL METHOD me->download_file_by_uuid
        EXPORTING
          server  = server
          iv_uuid = lv_uuid.
    ENDIF.
    RETURN.
  ENDIF.


* PDF存入系统表
  CLEAR lv_uuid.
  lv_regex = '^pdf=(.*)'.
  FIND REGEX lv_regex IN lv_query SUBMATCHES lv_uuid.
  IF sy-subrc = 0.
    IF lv_uuid IS INITIAL.
      server->response->set_status( code = 404 reason = '文件唯一标识未指定' ).
    ELSE.
      CALL METHOD me->download_file_by_pdf
        EXPORTING
          server  = server
          iv_uuid = lv_uuid.
    ENDIF.
    RETURN.
  ENDIF.
  
* else请求参数不支持
  server->response->set_status( code = 404 reason = '文件请求参数不支持' ).

1.读取文件服务器(AL11)
包含的数据库表ZBCT_APP_FILE,用来记录上传时对应的uuid和相应文件路径:
在这里插入图片描述

METHOD download_file_by_uuid.

  DATA:
    lt_return       TYPE bapiret2_t,
    lt_header       TYPE tihttpnvp,
    ls_header       TYPE ihttpnvp,
    lv_uuid         TYPE string,
    lv_file_name    TYPE string,
    lv_file_content TYPE xstring,
    lv_file_length  TYPE i,
    lv_content_type TYPE string,
    lv_failed       TYPE abap_bool.

* 获取文件唯一标识
  lv_uuid = iv_uuid.
  TRANSLATE lv_uuid TO LOWER CASE.

* 获取文件信息
  IF lv_uuid IS NOT INITIAL.
    SELECT SINGLE *
      FROM zbct_app_file
      INTO @DATA(ls_app_file)
     WHERE file_name = @lv_uuid.
    IF sy-subrc <> 0.
      CLEAR ls_app_file.
    ENDIF.
  ENDIF.

* 下载文件内容
  IF ls_app_file-file_fullname IS NOT INITIAL.
    CLEAR lt_return.
    CALL FUNCTION 'ZBC_FM_FILE_DOWNLOAD'
      EXPORTING
        iv_file_name    = ls_app_file-file_fullname
      IMPORTING
        et_return       = lt_return
        ev_file_content = lv_file_content
        ev_file_length  = lv_file_length.
    IF lv_file_content IS INITIAL.
      lv_failed = abap_true.
    ENDIF.
  ENDIF.

  IF lv_failed IS INITIAL.
* 文件名称
    lv_file_name = ls_app_file-file_name.
    IF ls_app_file-file_extension IS NOT INITIAL.
      lv_file_name = lv_file_name && '.' && ls_app_file-file_extension.
    ENDIF.

* 文件类型
    lv_content_type = ls_app_file-mime_type.
    IF lv_content_type IS INITIAL.
      lv_content_type = 'image/jpeg'.
    ENDIF.

* 返回文件名称
    CLEAR: lt_header, ls_header.
    ls_header-name = 'content-disposition'.
    ls_header-value = |outline; filename={ lv_file_name }|.
    server->response->set_header_fields( lt_header ).
  ENDIF.

  IF lv_failed IS NOT INITIAL.
    server->response->set_status( code = 404 reason = '文件不存在' ).
  ELSE.
    server->response->set_status( code = 200 reason = 'Ok' ).
    server->response->set_content_type( lv_content_type ).
    server->response->set_data( data = lv_file_content ).
  ENDIF.

ENDMETHOD.

内含的Function Module:ZBC_FM_FILE_DOWNLOAD 文件下载 代码如下

FUNCTION zbc_fm_file_download.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  IMPORTING
*"     REFERENCE(IV_FILE_NAME)
*"     REFERENCE(IV_GUI_DOWNLOAD) OPTIONAL
*"  EXPORTING
*"     REFERENCE(ET_RETURN) TYPE  BAPIRET2_T
*"     REFERENCE(EV_FILE_NAME)
*"     REFERENCE(EV_FILE_PATH)
*"     REFERENCE(EV_FILE_EXTENSION)
*"     REFERENCE(EV_FILE_CONTENT) TYPE  XSTRING
*"     REFERENCE(EV_FILE_LENGTH)
*"     REFERENCE(ET_SOLIX) TYPE  SOLIX_TAB
*"----------------------------------------------------------------------

  DATA:
    lt_solix               TYPE solix_tab,
    ls_solix               TYPE solix,
    lv_command             TYPE sxpgcolist-name,
    lv_parameters          TYPE sxpgcolist-parameters,
    lv_opsystem            TYPE sxpgcolist-opsystem,
    lv_status              TYPE extcmdexex-status,
    lv_exitcode            TYPE extcmdexex-exitcode,
    lt_protocol            TYPE STANDARD TABLE OF btcxpm,
    lv_file_name           TYPE string,
    lv_file_path           TYPE string,
    lv_full_name           TYPE string,
    lv_path_separator      TYPE dmc_mds_path_separator,
    lv_file_length         TYPE i,
    lv_max_length          TYPE i,
    lv_actual_length       TYPE i,
    lv_window_title        TYPE string,
    lv_default_file_name   TYPE string,
    lv_prompt_on_overwrite TYPE abap_bool,
    lv_path                TYPE string,
    lv_user_action         TYPE i,
    lv_regex               TYPE string,
    lv_failed              TYPE abap_bool,
    lv_message             TYPE string.

  CLEAR: et_return,
         ev_file_name,
         ev_file_path,
         ev_file_extension,
         ev_file_content,
         ev_file_length,
         et_solix,
         lv_failed.

* 文件全名
  IF iv_file_name IS INITIAL.
    APPEND VALUE #( type = 'E' id = '00' number = '398' message = '文件名不能为空' ) TO et_return.
    RETURN.
  ENDIF.
  lv_full_name = iv_file_name.

* 判断文件是否已经存在
  lv_command = 'DIR'.
  lv_opsystem = 'UNIX'.
  lv_parameters = lv_full_name.
  CLEAR: lt_protocol, lv_status, lv_exitcode.
  CALL FUNCTION 'SXPG_COMMAND_EXECUTE'
    EXPORTING
      commandname                   = lv_command
      additional_parameters         = lv_parameters
      operatingsystem               = lv_opsystem
    IMPORTING
      status                        = lv_status
      exitcode                      = lv_exitcode
    TABLES
      exec_protocol                 = lt_protocol
    EXCEPTIONS
      no_permission                 = 1
      command_not_found             = 2
      parameters_too_long           = 3
      security_risk                 = 4
      wrong_check_call_interface    = 5
      program_start_error           = 6
      program_termination_error     = 7
      x_error                       = 8
      parameter_expected            = 9
      too_many_parameters           = 10
      illegal_command               = 11
      wrong_asynchronous_parameters = 12
      cant_enq_tbtco_entry          = 13
      jobcount_generation_error     = 14
      OTHERS                        = 15.
  IF sy-subrc <> 0 OR lv_status <> 'O' OR lv_exitcode <> 0.
    et_return = VALUE #( ( type = 'E' id = '00' number = '398' message = '文件不存在' ) ).
    RETURN.
  ENDIF.

* 打开文件
  CLEAR lv_message.
  TRY.
      OPEN DATASET lv_full_name FOR INPUT IN BINARY MODE MESSAGE lv_message.
    CATCH cx_sy_file_open INTO DATA(lx_file_open).
      lv_failed = abap_true.
      lv_message = lx_file_open->get_text( ).
    CATCH cx_sy_codepage_converter_init INTO DATA(lx_codepage_converter_init).
      lv_failed = abap_true.
      lv_message = lx_codepage_converter_init->get_text( ).
    CATCH cx_sy_conversion_codepage INTO DATA(lx_conversion_codepage).
      lv_failed = abap_true.
      lv_message = lx_conversion_codepage->get_text( ).
    CATCH cx_sy_file_authority INTO DATA(lx_file_authority).
      lv_failed = abap_true.
      lv_message = lx_file_authority->get_text( ).
    CATCH cx_sy_pipes_not_supported INTO DATA(lx_pipes_not_supported).
      lv_failed = abap_true.
      lv_message = lx_pipes_not_supported->get_text( ).
    CATCH cx_sy_too_many_files INTO DATA(lx_too_many_files).
      lv_failed = abap_true.
      lv_message = lx_too_many_files->get_text( ).
    CATCH cx_root INTO DATA(lx_root).
      lv_failed = abap_true.
      lv_message = lx_root->get_text( ).
  ENDTRY.

  IF lv_failed IS NOT INITIAL.
    APPEND VALUE #( type = 'E' id = '00' number = '398' message = lv_message ) TO et_return.
    RETURN.
  ENDIF.

* 读文件
  CLEAR: lt_solix, lv_file_length.
  DO.
    CLEAR: ls_solix, lv_actual_length, lv_message.
    lv_max_length = 255.
    TRY.
        READ DATASET lv_full_name INTO ls_solix-line MAXIMUM LENGTH lv_max_length ACTUAL LENGTH lv_actual_length.
      CATCH cx_sy_codepage_converter_init INTO lx_codepage_converter_init.
        lv_failed = abap_true.
        lv_message = lx_codepage_converter_init->get_text( ).
      CATCH cx_sy_conversion_codepage INTO lx_conversion_codepage.
        lv_failed = abap_true.
        lv_message = lx_conversion_codepage->get_text( ).
      CATCH cx_sy_file_authority INTO lx_file_authority.
        lv_failed = abap_true.
        lv_message = lx_file_authority->get_text( ).
      CATCH cx_sy_file_io INTO DATA(lx_file_io).
        lv_failed = abap_true.
        lv_message = lx_file_io->get_text( ).
      CATCH cx_sy_file_open INTO lx_file_open.
        lv_failed = abap_true.
        lv_message = lx_file_open->get_text( ).
      CATCH cx_sy_file_open_mode INTO DATA(lx_file_open_mode).
        lv_failed = abap_true.
        lv_message = lx_file_open_mode->get_text( ).
      CATCH cx_sy_pipe_reopen INTO DATA(lx_pipe_reopen).
        lv_failed = abap_true.
        lv_message = lx_pipe_reopen->get_text( ).
      CATCH cx_root INTO lx_root.
        lv_failed = abap_true.
        lv_message = lx_root->get_text( ).
    ENDTRY.
    IF lv_failed IS NOT INITIAL.
      APPEND VALUE #( type = 'E' id = '00' number = '398' message = lv_message ) TO et_return.
      EXIT.
    ENDIF.
    IF lv_actual_length IS INITIAL.
      EXIT.
    ENDIF.
    ADD lv_actual_length TO lv_file_length.
    APPEND ls_solix TO lt_solix.
  ENDDO.

* 转换二进制
  IF lt_solix IS NOT INITIAL.
    CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
      EXPORTING
        input_length = lv_file_length
      IMPORTING
        buffer       = ev_file_content
      TABLES
        binary_tab   = lt_solix
      EXCEPTIONS
        failed       = 1
        OTHERS       = 2.
  ENDIF.

* 关闭文件
  CLEAR lv_message.
  TRY.
      CLOSE DATASET lv_full_name.
    CATCH cx_sy_file_close INTO DATA(lx_file_close).
      lv_failed = abap_true.
      lv_message = lx_file_close->get_text( ).
    CATCH cx_root INTO lx_root.
      lv_failed = abap_true.
      lv_message = lx_root->get_text( ).
  ENDTRY.

  IF lv_message IS NOT INITIAL.
    APPEND VALUE #( type = 'E' id = '00' number = '398' message = lv_message ) TO et_return.
  ENDIF.

  IF lv_failed IS INITIAL.
    ev_file_length = lv_file_length.
    et_solix = lt_solix.

* 文件路径分隔符
    CLEAR lv_path_separator.
    CALL FUNCTION 'DMC_MDS_GET_PATHSEPARATOR'
      IMPORTING
        ev_path_separator     = lv_path_separator
      EXCEPTIONS
        opsys_not_supported   = 1
        filesys_not_supported = 2
        OTHERS                = 3.
    IF sy-subrc <> 0.
      lv_path_separator = '/'.
    ENDIF.

    CLEAR: lv_file_path, lv_file_name.
    FIND FIRST OCCURRENCE OF lv_path_separator IN lv_full_name.
    IF sy-subrc = 0.
      lv_regex = '(.*)\\' && lv_path_separator && '(.*)'.
      FIND REGEX lv_regex IN lv_full_name SUBMATCHES lv_file_path lv_file_name.
    ELSE.
      lv_file_name = lv_full_name.
    ENDIF.
    ev_file_path = lv_file_path.

    FIND FIRST OCCURRENCE OF '.' IN lv_full_name.
    IF sy-subrc = 0.
      lv_regex = '(.*)\\.(.*)'.
      FIND REGEX lv_regex IN lv_file_name SUBMATCHES ev_file_name ev_file_extension.
    ELSE.
      ev_file_name = lv_full_name.
    ENDIF.

    APPEND VALUE #( type = 'S' id = '00' number = '398' message = '文件下载成功' ) TO et_return.
  ELSE.
    APPEND VALUE #( type = 'E' id = '00' number = '398' message = '文件下载失败' ) TO et_return.
  ENDIF.

  IF iv_gui_download IS NOT INITIAL.
* 选择保存路径
    lv_window_title = '选择文件夹'.
    lv_default_file_name = lv_file_name.
    lv_prompt_on_overwrite = abap_true.
    CLEAR: lv_file_name, lv_path, lv_file_path, lv_user_action.
    CALL METHOD cl_gui_frontend_services=>file_save_dialog
      EXPORTING
        window_title              = lv_window_title
        default_file_name         = lv_default_file_name
        prompt_on_overwrite       = lv_prompt_on_overwrite
      CHANGING
        filename                  = lv_file_name
        path                      = lv_path
        fullpath                  = lv_file_path
        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.
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE 'S' NUMBER sy-msgno DISPLAY LIKE 'E'
            WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      RETURN.
    ENDIF.

    IF lv_user_action <> cl_gui_frontend_services=>action_ok.
      RETURN.
    ENDIF.

* 保存
    CALL METHOD cl_gui_frontend_services=>gui_download
      EXPORTING
        bin_filesize            = lv_file_length
        filename                = lv_file_path
        filetype                = 'BIN'
      CHANGING
        data_tab                = lt_solix
      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.
      MESSAGE ID sy-msgid TYPE 'S' NUMBER sy-msgno DISPLAY LIKE 'E'
            WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      RETURN.
    ELSE.
      MESSAGE '文件保存成功' TYPE 'S'.
    ENDIF.
  ENDIF.

ENDFUNCTION.
  1. PDF直接存系统表
    建立数据库表ZBCT_APP_PDF,用来记录PDF文件的二进制流
    在这里插入图片描述
METHOD download_file_by_pdf.

  TYPES:
    BEGIN OF ty_file,
      uuid      TYPE string,
      file_name TYPE string,
    END OF ty_file.

  DATA:
    lt_return       TYPE bapiret2_t,
    lt_header       TYPE tihttpnvp,
    ls_header       TYPE ihttpnvp,
    lt_file         TYPE STANDARD TABLE OF ty_file,
    ls_file         TYPE ty_file,
    lv_json         TYPE string,
    lv_uuid         TYPE sysuuid_c36,
    lv_sysuuid_c32  TYPE sysuuid_c32,
    lv_file_name    TYPE string,
    lv_file_content TYPE xstring,
    lv_file_length  TYPE i,
    lv_content_type TYPE string,
    lv_failed       TYPE abap_bool.

* 获取文件唯一标识
  lv_uuid = iv_uuid.
  TRANSLATE lv_uuid TO UPPER CASE.

  IF lv_uuid = 'HELP'.
    SELECT *
      FROM zbct_app_pdf
      INTO TABLE @DATA(lt_app_pdf)
     WHERE file_cat = @lv_uuid.

    CLEAR lt_file.
    LOOP AT lt_app_pdf INTO DATA(ls_app_pdf).
      lv_sysuuid_c32 = ls_app_pdf-guid.
      CALL METHOD cl_system_uuid=>convert_uuid_c32_static
        EXPORTING
          uuid     = lv_sysuuid_c32
        IMPORTING
          uuid_c36 = DATA(lv_uuid_c36).
      CLEAR ls_file.
      ls_file-uuid = lv_uuid_c36.
      TRANSLATE ls_file-uuid TO LOWER CASE.
      ls_file-file_name = ls_app_pdf-file_name.
      APPEND ls_file TO lt_file.
    ENDLOOP.

    SORT lt_file by file_name.

    CLEAR lv_json.
    CALL FUNCTION 'ZBC_FM_JSON_SERIALIZE'
      EXPORTING
        is_data = lt_file
      IMPORTING
        ev_json = lv_json.

    server->response->set_status( code = 200 reason = 'Ok' ).
    server->response->set_content_type( 'application/json' ).
    server->response->set_cdata( data = lv_json ).

    RETURN.
  ENDIF.

* 获取文件信息
  IF lv_uuid IS NOT INITIAL.
    CALL METHOD cl_system_uuid=>convert_uuid_c36_static
      EXPORTING
        uuid     = lv_uuid
      IMPORTING
        uuid_x16 = DATA(lv_guid).
    IF lv_guid IS NOT INITIAL.
      SELECT SINGLE *
        FROM zbct_app_pdf
        INTO @ls_app_pdf
       WHERE guid = @lv_guid.
      IF sy-subrc <> 0.
        CLEAR ls_app_pdf.
      ENDIF.
    ENDIF.
    IF ls_app_pdf IS INITIAL.
      lv_failed = abap_true.
    ENDIF.
  ENDIF.

  IF lv_failed IS INITIAL.
* 文件名称
    lv_file_name = ls_app_pdf-file_name.

* 文件类型
    lv_content_type = 'application/pdf'.

* 文件内容
    lv_file_content = ls_app_pdf-file_content.

* 返回文件名称
    CLEAR: lt_header, ls_header.
    ls_header-name = 'content-disposition'.
    ls_header-value = |outline; filename={ lv_file_name }|.
    server->response->set_header_fields( lt_header ).

    server->response->set_status( code = 200 reason = 'Ok' ).
    server->response->set_content_type( lv_content_type ).
    server->response->set_data( data = lv_file_content ).
  ELSE.
    server->response->set_status( code = 404 reason = '文件不存在' ).
  ENDIF.

ENDMETHOD.

上传文件

上传至文件服务器的对应目录下:
设计的文件传参格式如下图:
在这里插入图片描述

METHOD upload_file.

  DATA: BEGIN OF ls_action.
          INCLUDE TYPE zbc_cl_odata_file_mpc_ext=>ts_action.
  DATA:
          file_set TYPE zbc_cl_odata_file_mpc_ext=>tt_file,
        END OF ls_action,
        ls_file                  TYPE zbc_cl_odata_file_mpc_ext=>ts_file,
        lt_return                TYPE bapiret2_t,
        lt_header                TYPE tihttpnvp,
        ls_header                TYPE ihttpnvp,
        lv_content_type          TYPE string,
        lt_app_file              TYPE STANDARD TABLE OF zbct_app_file,
        ls_app_file              TYPE zbct_app_file,
        lv_upload_file_name      TYPE string,
        lv_upload_file_extension TYPE string,
        lv_guid                  TYPE guid,
        lv_full_name             TYPE string,
        lv_file_name             TYPE string,
        lv_file_path             TYPE string,
        lv_file_root_path        TYPE string,
        lv_file_sub_path         TYPE string,
        lv_file_extension        TYPE string,
        lv_file_content          TYPE xstring,
        lv_file_length           TYPE i,
        lv_json                  TYPE string,
        lv_regex                 TYPE string,
        lv_failed                TYPE abap_bool.

* 获取文件名称和文件扩展名
  CLEAR: lv_file_name, lv_upload_file_name, lv_upload_file_extension.
  lv_upload_file_name = server->request->get_header_field( 'SLUG' ).
  FIND FIRST OCCURRENCE OF '.' IN lv_upload_file_name.
  IF sy-subrc = 0.
    lv_regex = '(.*)\\.(.*)'.
    FIND REGEX lv_regex IN lv_upload_file_name SUBMATCHES lv_file_name lv_upload_file_extension.
  ELSE.
    lv_file_name = lv_upload_file_name.
  ENDIF.

* 生成文件唯一标识
  CLEAR: lt_return.
  CALL FUNCTION 'ZBC_FM_FILE_APP_GEN_FILE_NAME'
    EXPORTING
      iv_file_extension = lv_upload_file_extension
      iv_date           = sy-datum
    IMPORTING
      et_return         = lt_return
      ev_guid           = lv_guid
      ev_full_name      = lv_full_name
      ev_file_name      = lv_file_name
      ev_file_path      = lv_file_path
      ev_file_root_path = lv_file_root_path
      ev_file_sub_path  = lv_file_sub_path
      ev_file_extension = lv_file_extension.

* 获取文件内容
  DATA(lo_multipart) = server->request->get_multipart( 1 ).
  IF lo_multipart IS BOUND.
    lv_file_content = lo_multipart->get_data( ).
    lv_content_type = lo_multipart->get_content_type( ).
    IF lv_content_type = 'application/octet-stream'.
      lv_content_type = 'image/jpeg'.
    ENDIF.
  ENDIF.

  CLEAR: lt_return, lv_file_length.
  CALL FUNCTION 'ZBC_FM_FILE_UPLOAD'
    EXPORTING
      iv_file_name      = lv_file_name
      iv_file_extension = lv_file_extension
      iv_file_path      = lv_file_path
      iv_file_content   = lv_file_content
    IMPORTING
      et_return         = lt_return
      ev_file_length    = lv_file_length.

  CLEAR: lt_app_file, ls_app_file.
  ls_app_file-guid = lv_guid.
  ls_app_file-file_name = lv_file_name.
  ls_app_file-file_extension = lv_file_extension.
  ls_app_file-file_length = lv_file_length.
  ls_app_file-file_path = lv_file_path.
  ls_app_file-file_rootpath = lv_file_root_path.
  ls_app_file-file_subpath = lv_file_sub_path.
  ls_app_file-file_fullname = lv_full_name.
  ls_app_file-upload_file_name = lv_upload_file_name.
  ls_app_file-mime_type = lv_content_type.
  ls_app_file-created_date = sy-datum.
  ls_app_file-created_time = sy-uzeit.
  ls_app_file-created_user = sy-uname.
  APPEND ls_app_file TO lt_app_file.
  MODIFY zbct_app_file FROM TABLE lt_app_file.
  COMMIT WORK AND WAIT.

* 返回文件唯一标识
  CLEAR ls_file.
  ls_file-uuid = lv_file_name.
  ls_file-file_name = lv_upload_file_name.
  ls_file-mime_type = lv_content_type.

  CLEAR ls_action.
  ls_action-name = 'File'.
  ls_action-method = 'Upload'.
  ls_action-param = lv_file_name.
  ls_action-desc = lv_upload_file_name.
  APPEND ls_file TO ls_action-file_set.

  CLEAR lv_json.
  CALL FUNCTION 'ZBC_FM_JSON_SERIALIZE'
    EXPORTING
      is_data = ls_action
    IMPORTING
      ev_json = lv_json.

  IF lv_failed IS NOT INITIAL.
    server->response->set_status( code = 500 reason = '文件上传失败' ).
  ELSE.
    lv_content_type = 'application/json'.
    server->response->set_status( code = 200 reason = 'Ok' ).
    server->response->set_content_type( lv_content_type ).
    server->response->set_cdata( data = lv_json ).
  ENDIF.

ENDMETHOD.

对应Function Module代码如下:
ZBC_FM_FILE_APP_GEN_FILE_NAME

FUNCTION zbc_fm_file_app_gen_file_name.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  IMPORTING
*"     REFERENCE(IV_FILE_NAME) OPTIONAL
*"     REFERENCE(IV_FILE_EXTENSION) DEFAULT 'jpg'
*"     REFERENCE(IV_DATE) TYPE  SY-DATUM DEFAULT SY-DATUM
*"  EXPORTING
*"     REFERENCE(ET_RETURN) TYPE  BAPIRET2_T
*"     REFERENCE(EV_GUID)
*"     REFERENCE(EV_FULL_NAME)
*"     REFERENCE(EV_FILE_NAME)
*"     REFERENCE(EV_FILE_EXTENSION)
*"     REFERENCE(EV_FILE_PATH)
*"     REFERENCE(EV_FILE_ROOT_PATH)
*"     REFERENCE(EV_FILE_SUB_PATH)
*"----------------------------------------------------------------------

  DATA:
    lv_file_path      TYPE string,
    lv_path_separator TYPE dmc_mds_path_separator.

  CLEAR: et_return,
         ev_guid,
         ev_full_name,
         ev_file_name,
         ev_file_path,
         ev_file_extension.

* 获取APP文件夹
  CALL FUNCTION 'ZBC_FM_FILE_APP_DIR_HOME'
    IMPORTING
      ev_dir = lv_file_path.
  IF lv_file_path IS INITIAL.
    APPEND VALUE #( type = 'E' id = '00' number = '398' message = '文件服务器目录未维护' ) TO et_return.
    RETURN.
  ENDIF.

* 文件路径分隔符
  CLEAR lv_path_separator.
  CALL FUNCTION 'DMC_MDS_GET_PATHSEPARATOR'
    IMPORTING
      ev_path_separator     = lv_path_separator
    EXCEPTIONS
      opsys_not_supported   = 1
      filesys_not_supported = 2
      OTHERS                = 3.
  IF sy-subrc <> 0.
    lv_path_separator = '/'.
  ENDIF.

* 取日期作为子文件夹
  ev_file_root_path = lv_file_path.
  ev_file_sub_path = iv_date.
  ev_file_path = lv_file_path && lv_path_separator && ev_file_sub_path.

* 按uuid生成文件名称
  IF iv_file_name IS NOT INITIAL.
    ev_file_name = iv_file_name.
  ELSE.
    CALL FUNCTION 'ZBC_FM_CREATE_GUID'
      IMPORTING
        ev_guid = ev_guid
        ev_uuid = ev_file_name.
  ENDIF.

* 文件全名
  ev_full_name = ev_file_path && lv_path_separator && ev_file_name.
  IF iv_file_extension IS NOT INITIAL.
    ev_file_extension = iv_file_extension.
    ev_full_name = ev_full_name && '.' && ev_file_extension.
  ENDIF.

ENDFUNCTION.

ZBC_FM_FILE_APP_DIR_HOME 获取APP文件夹

FUNCTION zbc_fm_file_app_dir_home.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  EXPORTING
*"     REFERENCE(EV_DIR)
*"----------------------------------------------------------------------

  CLEAR ev_dir.

* AL11目录
  SELECT SINGLE dirname
    FROM user_dir
    INTO @ev_dir
   WHERE aliass = 'ZAPP_FILE'.

ENDFUNCTION.

ZBC_FM_CREATE_GUID 生成GUID

FUNCTION zbc_fm_create_guid.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  EXPORTING
*"     REFERENCE(EV_GUID)
*"     REFERENCE(EV_UUID)
*"----------------------------------------------------------------------

  CLEAR: ev_guid, ev_uuid.

  TRY.
      DATA(lv_uuid) = cl_system_uuid=>create_uuid_c32_static( ).
    CATCH cx_uuid_error.
  ENDTRY.

  IF ev_guid IS SUPPLIED.
    ev_guid = lv_uuid.
  ENDIF.

  IF ev_uuid IS SUPPLIED.
    CALL METHOD cl_system_uuid=>convert_uuid_c32_static
      EXPORTING
        uuid     = lv_uuid
      IMPORTING
        uuid_c36 = DATA(lv_uuid_c36).
    ev_uuid = lv_uuid_c36.
    TRANSLATE ev_uuid TO LOWER CASE.
  ENDIF.

ENDFUNCTION.

ZBC_FM_FILE_UPLOAD 文件上传

FUNCTION zbc_fm_file_upload.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  IMPORTING
*"     REFERENCE(IV_FILE_NAME) OPTIONAL
*"     REFERENCE(IV_FILE_EXTENSION) OPTIONAL
*"     REFERENCE(IV_FILE_PATH)
*"     REFERENCE(IV_FILE_CONTENT) TYPE  XSTRING OPTIONAL
*"  EXPORTING
*"     REFERENCE(ET_RETURN) TYPE  BAPIRET2_T
*"     REFERENCE(EV_FILE_NAME)
*"     REFERENCE(EV_FILE_LENGTH)
*"----------------------------------------------------------------------

  DATA:
    lt_solix          TYPE solix_tab,
    lv_file_name      TYPE string,
    lv_file_path      TYPE string,
    lv_full_name      TYPE string,
    lv_path_separator TYPE dmc_mds_path_separator,
    lv_uuid           TYPE string,
    lv_file_length    TYPE i,
    lv_length         TYPE i,
    lv_failed         TYPE abap_bool,
    lv_message        TYPE string.

  CLEAR: et_return, ev_file_name, ev_file_length, lv_failed.

* 生成uuid文件名
  CLEAR lv_file_name.
  IF iv_file_name IS NOT INITIAL.
    lv_file_name = iv_file_name.
  ELSE.
    CALL FUNCTION 'ZBC_FM_CREATE_GUID'
      IMPORTING
        ev_uuid = lv_uuid.
    lv_file_name = lv_uuid.
  ENDIF.

* 创建文件夹
  lv_file_path = iv_file_path.
  CALL FUNCTION 'ZBC_FM_FILE_MKDIR'
    EXPORTING
      iv_dir    = lv_file_path
    IMPORTING
      et_return = et_return.
  LOOP AT et_return TRANSPORTING NO FIELDS
    WHERE type CA 'EAX'.
    EXIT.
  ENDLOOP.
  IF sy-subrc = 0.
    RETURN.
  ENDIF.

* 文件路径分隔符
  CLEAR lv_path_separator.
  CALL FUNCTION 'DMC_MDS_GET_PATHSEPARATOR'
    IMPORTING
      ev_path_separator     = lv_path_separator
    EXCEPTIONS
      opsys_not_supported   = 1
      filesys_not_supported = 2
      OTHERS                = 3.
  IF sy-subrc <> 0.
    lv_path_separator = '/'.
  ENDIF.

* 文件全名
  lv_full_name = lv_file_path && lv_path_separator && lv_file_name.
  IF iv_file_extension IS NOT INITIAL.
    lv_full_name = lv_full_name && '.' && iv_file_extension.
  ENDIF.

* 打开文件
  CLEAR lv_message.
  TRY.
      OPEN DATASET lv_full_name FOR OUTPUT IN BINARY MODE MESSAGE lv_message.
    CATCH cx_sy_file_open INTO DATA(lx_file_open).
      lv_failed = abap_true.
      lv_message = lx_file_open->get_text( ).
    CATCH cx_sy_codepage_converter_init INTO DATA(lx_codepage_converter_init).
      lv_failed = abap_true.
      lv_message = lx_codepage_converter_init->get_text( ).
    CATCH cx_sy_conversion_codepage INTO DATA(lx_conversion_codepage).
      lv_failed = abap_true.
      lv_message = lx_conversion_codepage->get_text( ).
    CATCH cx_sy_file_authority INTO DATA(lx_file_authority).
      lv_failed = abap_true.
      lv_message = lx_file_authority->get_text( ).
    CATCH cx_sy_pipes_not_supported INTO DATA(lx_pipes_not_supported).
      lv_failed = abap_true.
      lv_message = lx_pipes_not_supported->get_text( ).
    CATCH cx_sy_too_many_files INTO DATA(lx_too_many_files).
      lv_failed = abap_true.
      lv_message = lx_too_many_files->get_text( ).
    CATCH cx_root INTO DATA(lx_root).
      lv_failed = abap_true.
      lv_message = lx_root->get_text( ).
  ENDTRY.

  IF lv_failed IS NOT INITIAL.
    APPEND VALUE #( type = 'E' id = '00' number = '398' message = lv_message ) TO et_return.
    RETURN.
  ENDIF.

* 转换二进制
  CLEAR: lt_solix, lv_file_length.
  CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
    EXPORTING
      buffer        = iv_file_content
    IMPORTING
      output_length = lv_file_length
    TABLES
      binary_tab    = lt_solix.

  ev_file_name = lv_file_name.
  ev_file_length = lv_file_length.

* 写文件
  LOOP AT lt_solix INTO DATA(ls_solix).
    DESCRIBE FIELD ls_solix-line LENGTH lv_length IN BYTE MODE.
    IF lv_length > lv_file_length.
      lv_length = lv_file_length.
    ELSE.
      SUBTRACT lv_length FROM lv_file_length.
    ENDIF.
    CHECK lv_length > 0.
    CLEAR lv_message.
    TRY.
        TRANSFER ls_solix-line TO lv_full_name LENGTH lv_length.
      CATCH cx_sy_codepage_converter_init INTO lx_codepage_converter_init.
        lv_failed = abap_true.
        lv_message = lx_codepage_converter_init->get_text( ).
      CATCH cx_sy_conversion_codepage INTO lx_conversion_codepage.
        lv_failed = abap_true.
        lv_message = lx_conversion_codepage->get_text( ).
      CATCH cx_sy_file_authority INTO lx_file_authority.
        lv_failed = abap_true.
        lv_message = lx_file_authority->get_text( ).
      CATCH cx_sy_file_io INTO DATA(lx_file_io).
        lv_failed = abap_true.
        lv_message = lx_file_io->get_text( ).
      CATCH cx_sy_file_open INTO lx_file_open.
        lv_failed = abap_true.
        lv_message = lx_file_open->get_text( ).
      CATCH cx_sy_file_open_mode INTO DATA(lx_file_open_mode).
        lv_failed = abap_true.
        lv_message = lx_file_open_mode->get_text( ).
      CATCH cx_sy_pipe_reopen INTO DATA(lx_pipe_reopen).
        lv_failed = abap_true.
        lv_message = lx_pipe_reopen->get_text( ).
      CATCH cx_sy_too_many_files INTO lx_too_many_files.
        lv_failed = abap_true.
        lv_message = lx_too_many_files->get_text( ).
      CATCH cx_root INTO lx_root.
        lv_failed = abap_true.
        lv_message = lx_root->get_text( ).
    ENDTRY.
    IF lv_failed IS NOT INITIAL.
      APPEND VALUE #( type = 'E' id = '00' number = '398' message = lv_message ) TO et_return.
      EXIT.
    ENDIF.
  ENDLOOP.

* 关闭文件
  CLEAR lv_message.
  TRY.
      CLOSE DATASET lv_full_name.
    CATCH cx_sy_file_close INTO DATA(lx_file_close).
      lv_failed = abap_true.
      lv_message = lx_file_close->get_text( ).
    CATCH cx_root INTO lx_root.
      lv_failed = abap_true.
      lv_message = lx_root->get_text( ).
  ENDTRY.

  IF lv_message IS NOT INITIAL.
    APPEND VALUE #( type = 'E' id = '00' number = '398' message = lv_message ) TO et_return.
  ENDIF.

  IF lv_failed IS INITIAL.
    APPEND VALUE #( type = 'S' id = '00' number = '398' message = '文件上传成功' ) TO et_return.
  ELSE.
    APPEND VALUE #( type = 'E' id = '00' number = '398' message = '文件上传失败' ) TO et_return.
  ENDIF.

ENDFUNCTION.

ZBC_FM_FILE_MKDIR 创建文件夹

FUNCTION zbc_fm_file_mkdir.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  IMPORTING
*"     REFERENCE(IV_DIR)
*"  EXPORTING
*"     REFERENCE(ET_RETURN) TYPE  BAPIRET2_T
*"----------------------------------------------------------------------

  DATA:
    lv_command  TYPE sxpgcolist-name,
    lv_dir      TYPE sxpgcolist-parameters,
    lv_opsystem TYPE sxpgcolist-opsystem,
    lv_status   TYPE extcmdexex-status,
    lv_exitcode TYPE extcmdexex-exitcode,
    lt_protocol TYPE STANDARD TABLE OF btcxpm.

  CLEAR et_return.

* 文件夹
  lv_dir = iv_dir.
  IF lv_dir IS INITIAL.
    et_return = VALUE #( ( type = 'S' id = '00' number = '398' message = '文件夹不能为空' ) ).
    RETURN.
  ENDIF.

* 判断文件夹是否已经存在
  lv_command = 'DIR'.
  lv_opsystem = 'UNIX'.
  CLEAR: lt_protocol, lv_status, lv_exitcode.
  CALL FUNCTION 'SXPG_COMMAND_EXECUTE'
    EXPORTING
      commandname                   = lv_command
      additional_parameters         = lv_dir
      operatingsystem               = lv_opsystem
    IMPORTING
      status                        = lv_status
      exitcode                      = lv_exitcode
    TABLES
      exec_protocol                 = lt_protocol
    EXCEPTIONS
      no_permission                 = 1
      command_not_found             = 2
      parameters_too_long           = 3
      security_risk                 = 4
      wrong_check_call_interface    = 5
      program_start_error           = 6
      program_termination_error     = 7
      x_error                       = 8
      parameter_expected            = 9
      too_many_parameters           = 10
      illegal_command               = 11
      wrong_asynchronous_parameters = 12
      cant_enq_tbtco_entry          = 13
      jobcount_generation_error     = 14
      OTHERS                        = 15.
  IF sy-subrc = 0 AND lv_status = 'O' AND lv_exitcode = 0.
    et_return = VALUE #( ( type = 'S' id = '00' number = '398' message = '文件夹已存在' ) ).
    RETURN.
  ENDIF.

  lv_command = 'ZMKDIR'.
  lv_opsystem = 'ANYOS'.
  CLEAR: lt_protocol, lv_status, lv_exitcode.
  CALL FUNCTION 'SXPG_COMMAND_EXECUTE'
    EXPORTING
      commandname                   = lv_command
      additional_parameters         = lv_dir
      operatingsystem               = lv_opsystem
    IMPORTING
      status                        = lv_status
      exitcode                      = lv_exitcode
    TABLES
      exec_protocol                 = lt_protocol
    EXCEPTIONS
      no_permission                 = 1
      command_not_found             = 2
      parameters_too_long           = 3
      security_risk                 = 4
      wrong_check_call_interface    = 5
      program_start_error           = 6
      program_termination_error     = 7
      x_error                       = 8
      parameter_expected            = 9
      too_many_parameters           = 10
      illegal_command               = 11
      wrong_asynchronous_parameters = 12
      cant_enq_tbtco_entry          = 13
      jobcount_generation_error     = 14
      OTHERS                        = 15.
  IF sy-subrc = 0 AND lv_status = 'O' AND lv_exitcode = 0.
    et_return = VALUE #( ( type = 'S' id = '00' number = '398' message = '文件夹创建成功' ) ).
  ELSE.
    et_return = VALUE #( ( type = 'E' id = '00' number = '398' message = '文件夹创建失败' ) ).
    LOOP AT lt_protocol INTO DATA(ls_protocol).
      APPEND VALUE #( type = 'E' id = '00' number = '398' message = ls_protocol-message ) TO et_return.
    ENDLOOP.
  ENDIF.

ENDFUNCTION.

以上就是此次需求的 文件上传和下载 的思路,如果对你有帮助,希望动动你的小手帮忙点赞,感谢!

以上是关于SAP ABAP 上传下载文件到文件服务器的主要内容,如果未能解决你的问题,请参考以下文章

ABAP写数据到SAP服务器文件并读取

SAP附件UI里选择的文件是如何传到ABAP服务器的

在SAP ABAP程序编译过程中 【FTP文件上传,下载,在哪句中需要做异常捕获?】谢谢!

sap abap报表显示的问题

ABAP系列SAP 一个完整的SAP的Abap例子(idoc,edi文件的相互转换)

在没有 SAP 系统的情况下查看传输请求内容?