SAP Document Management Service Integration in ABAP Cloud

kategorie
Integration
Veröffentlicht
autor
Johannes

Der SAP Document Management Service (DMS) ist ein BTP-Service zur zentralen Verwaltung von Dokumenten und Dateien. Er bietet eine REST-API für Upload, Download, Versionierung und Metadatenverwaltung – ideal für RAP-Anwendungen, die Dateianhänge verwalten müssen.

Architektur und Konzept

Der Document Management Service fungiert als zentrale Dokumentenablage in der SAP Business Technology Platform:

┌─────────────────────────────────────────────────────────────────────────────┐
│ SAP Document Management Service │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ ABAP Cloud Anwendung │ │
│ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │
│ │ │ RAP Business │ │ HTTP Client │ │ │
│ │ │ Object │──│ (REST Calls) │ │ │
│ │ └─────────────────────┘ └──────────┬──────────┘ │ │
│ └───────────────────────────────────────┼──────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────────────────┼──────────────────────────────┐ │
│ │ Communication Arrangement │ │
│ │ ┌─────────────────────┐ ┌──────────┴──────────┐ │ │
│ │ │ Outbound Service │ │ OAuth2 Token │ │ │
│ │ │ (Destination) │ │ Management │ │ │
│ │ └─────────────────────┘ └─────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────────────────┼──────────────────────────────┐ │
│ │ SAP DMS (BTP Service) │ │
│ │ ┌─────────────────────┐ ┌──────────┴──────────┐ │ │
│ │ │ Repository API │ │ Object Store │ │ │
│ │ │ (REST) │ │ (Speicher) │ │ │
│ │ └─────────────────────┘ └─────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘

Vorteile des DMS

AspektBeschreibung
Zentrale AblageAlle Dokumente an einem Ort
VersionierungAutomatische Versionskontrolle
MetadatenStrukturierte Dokumenteigenschaften
SucheVolltextsuche in Dokumenten
SicherheitZugriffssteuerung und Audit-Trail
SkalierbarkeitCloud-native Architektur

Setup und Konfiguration

1. DMS Service in BTP aktivieren

Im BTP Cockpit muss der Document Management Service aktiviert und ein Repository erstellt werden:

BTP Cockpit → Subaccount → Service Marketplace
→ Document Management Service → Create Instance
Instance Name: my-dms-instance
Plan: standard
Repository: my-repository

2. Communication Arrangement erstellen

In ABAP Cloud wird die Verbindung über ein Communication Arrangement konfiguriert:

" Communication Scenario für DMS
" Wird im ADT angelegt unter:
" Source Library → Communication Scenarios

Communication Scenario Definition (sap_com_0502):

Scenario ID: Z_DMS_INTEGRATION
Description: SAP Document Management Service Integration
Outbound Services:
- HTTP Service: SAP_COM_0502 (Document Management)
- OAuth2 Authentication

3. Destination im BTP Cockpit

┌──────────────────────────────────────────────────────────────────┐
│ Destination Configuration │
├──────────────────────────────────────────────────────────────────┤
│ Name: DMS_API │
│ Type: HTTP │
│ URL: https://api-sdm-di.cfapps.eu10.hana.ondemand.com │
│ Proxy Type: Internet │
│ Authentication: OAuth2ClientCredentials │
│ │
│ ┌─ OAuth2 Settings ───────────────────────────────────────────┐ │
│ │ Client ID: <dms-client-id> │ │
│ │ Client Secret: ******** │ │
│ │ Token URL: https://<subaccount>.authentication.eu10... │ │
│ └──────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘

4. Communication Arrangement in ABAP

" Maintain Communication Arrangement in ADT oder Fiori App
" App: Maintain Communication Arrangements
Arrangement ID: Z_DMS_ARRANGEMENT
Scenario: Z_DMS_INTEGRATION
Communication System: DMS_SYSTEM
Outbound Service:
- Service ID: SAP_COM_0502
- Destination: DMS_API

DMS Client Klasse

Eine zentrale Klasse kapselt die DMS-Kommunikation:

CLASS zcl_dms_client DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_document,
id TYPE string,
name TYPE string,
content_type TYPE string,
size TYPE i,
created_at TYPE timestamp,
modified_at TYPE timestamp,
version TYPE string,
END OF ty_document,
tt_documents TYPE STANDARD TABLE OF ty_document WITH EMPTY KEY.
TYPES:
BEGIN OF ty_upload_result,
success TYPE abap_bool,
document_id TYPE string,
message TYPE string,
END OF ty_upload_result.
METHODS:
constructor
RAISING cx_http_dest_provider_error,
upload_document
IMPORTING
iv_filename TYPE string
iv_content_type TYPE string
iv_content TYPE xstring
iv_folder_id TYPE string OPTIONAL
RETURNING
VALUE(rs_result) TYPE ty_upload_result
RAISING
cx_web_http_client_error,
download_document
IMPORTING
iv_document_id TYPE string
RETURNING
VALUE(rv_content) TYPE xstring
RAISING
cx_web_http_client_error,
list_documents
IMPORTING
iv_folder_id TYPE string OPTIONAL
iv_search TYPE string OPTIONAL
RETURNING
VALUE(rt_documents) TYPE tt_documents
RAISING
cx_web_http_client_error,
delete_document
IMPORTING
iv_document_id TYPE string
RETURNING
VALUE(rv_success) TYPE abap_bool
RAISING
cx_web_http_client_error.
PRIVATE SECTION.
DATA: mo_http_client TYPE REF TO if_web_http_client,
mv_base_url TYPE string,
mv_repository TYPE string.
METHODS:
create_http_client
RAISING cx_http_dest_provider_error,
parse_document_response
IMPORTING
iv_json TYPE string
RETURNING
VALUE(rs_document) TYPE ty_document,
parse_documents_response
IMPORTING
iv_json TYPE string
RETURNING
VALUE(rt_documents) TYPE tt_documents.
ENDCLASS.
CLASS zcl_dms_client IMPLEMENTATION.
METHOD constructor.
mv_repository = 'my-repository'.
create_http_client( ).
ENDMETHOD.
METHOD create_http_client.
" HTTP Destination aus Communication Arrangement abrufen
DATA(lo_destination) = cl_http_destination_provider=>create_by_comm_arrangement(
comm_scenario = 'Z_DMS_INTEGRATION'
service_id = 'SAP_COM_0502'
).
mo_http_client = cl_web_http_client_manager=>create_by_http_destination(
i_destination = lo_destination
).
" Base URL für DMS API
mv_base_url = '/browser/' && mv_repository && '/root'.
ENDMETHOD.
METHOD upload_document.
DATA: lv_url TYPE string.
" URL für Upload erstellen
IF iv_folder_id IS NOT INITIAL.
lv_url = mv_base_url && '/' && iv_folder_id.
ELSE.
lv_url = mv_base_url.
ENDIF.
" Request vorbereiten
DATA(lo_request) = mo_http_client->get_http_request( ).
lo_request->set_uri_path( lv_url ).
lo_request->set_header_field(
i_name = 'Content-Type'
i_value = 'multipart/form-data'
).
" Multipart Body erstellen
DATA: lv_boundary TYPE string VALUE '----WebKitFormBoundary7MA4YWxkTrZu0gW'.
DATA(lv_body) = |--{ lv_boundary }{ cl_abap_char_utilities=>cr_lf }| &&
|Content-Disposition: form-data; name="file"; filename="{ iv_filename }"{ cl_abap_char_utilities=>cr_lf }| &&
|Content-Type: { iv_content_type }{ cl_abap_char_utilities=>cr_lf }| &&
|{ cl_abap_char_utilities=>cr_lf }|.
" Content als Binary anhängen
DATA(lv_body_xstring) = cl_abap_conv_codepage=>create_out( )->convert( lv_body ).
lv_body_xstring = lv_body_xstring && iv_content.
DATA(lv_end) = |{ cl_abap_char_utilities=>cr_lf }--{ lv_boundary }--|.
lv_body_xstring = lv_body_xstring && cl_abap_conv_codepage=>create_out( )->convert( lv_end ).
lo_request->set_binary_body( lv_body_xstring ).
lo_request->set_header_field(
i_name = 'Content-Type'
i_value = |multipart/form-data; boundary={ lv_boundary }|
).
" Request senden
DATA(lo_response) = mo_http_client->execute( if_web_http_client=>post ).
DATA(lv_status) = lo_response->get_status( )-code.
IF lv_status = 201.
rs_result-success = abap_true.
" Document ID aus Response extrahieren
DATA(lv_json) = lo_response->get_text( ).
DATA(ls_doc) = parse_document_response( lv_json ).
rs_result-document_id = ls_doc-id.
rs_result-message = 'Upload erfolgreich'.
ELSE.
rs_result-success = abap_false.
rs_result-message = |Upload fehlgeschlagen: HTTP { lv_status }|.
ENDIF.
ENDMETHOD.
METHOD download_document.
DATA(lv_url) = mv_base_url && '/' && iv_document_id && '?cmisselector=content'.
DATA(lo_request) = mo_http_client->get_http_request( ).
lo_request->set_uri_path( lv_url ).
DATA(lo_response) = mo_http_client->execute( if_web_http_client=>get ).
DATA(lv_status) = lo_response->get_status( )-code.
IF lv_status = 200.
rv_content = lo_response->get_binary_body( ).
ELSE.
RAISE EXCEPTION TYPE cx_web_http_client_error.
ENDIF.
ENDMETHOD.
METHOD list_documents.
DATA(lv_url) = mv_base_url && '?cmisselector=children'.
IF iv_folder_id IS NOT INITIAL.
lv_url = mv_base_url && '/' && iv_folder_id && '?cmisselector=children'.
ENDIF.
IF iv_search IS NOT INITIAL.
lv_url = lv_url && '&filter=' && cl_web_http_utility=>escape_url( iv_search ).
ENDIF.
DATA(lo_request) = mo_http_client->get_http_request( ).
lo_request->set_uri_path( lv_url ).
DATA(lo_response) = mo_http_client->execute( if_web_http_client=>get ).
DATA(lv_status) = lo_response->get_status( )-code.
IF lv_status = 200.
DATA(lv_json) = lo_response->get_text( ).
rt_documents = parse_documents_response( lv_json ).
ENDIF.
ENDMETHOD.
METHOD delete_document.
DATA(lv_url) = mv_base_url && '/' && iv_document_id.
DATA(lo_request) = mo_http_client->get_http_request( ).
lo_request->set_uri_path( lv_url ).
DATA(lo_response) = mo_http_client->execute( if_web_http_client=>delete ).
DATA(lv_status) = lo_response->get_status( )-code.
rv_success = xsdbool( lv_status = 204 OR lv_status = 200 ).
ENDMETHOD.
METHOD parse_document_response.
" JSON Response parsen
" Beispiel: {"objectId":"doc123","name":"test.pdf","size":12345}
DATA: lr_data TYPE REF TO data.
/ui2/cl_json=>deserialize(
EXPORTING
json = iv_json
CHANGING
data = lr_data
).
" Feldzuordnung
FIELD-SYMBOLS: <ls_data> TYPE any.
ASSIGN lr_data->* TO <ls_data>.
IF sy-subrc = 0.
ASSIGN COMPONENT 'objectId' OF STRUCTURE <ls_data> TO FIELD-SYMBOL(<lv_id>).
IF sy-subrc = 0.
rs_document-id = <lv_id>.
ENDIF.
ASSIGN COMPONENT 'name' OF STRUCTURE <ls_data> TO FIELD-SYMBOL(<lv_name>).
IF sy-subrc = 0.
rs_document-name = <lv_name>.
ENDIF.
ENDIF.
ENDMETHOD.
METHOD parse_documents_response.
" JSON Array parsen
DATA: lr_data TYPE REF TO data.
/ui2/cl_json=>deserialize(
EXPORTING
json = iv_json
CHANGING
data = lr_data
).
" Array durchlaufen und Dokumente extrahieren
FIELD-SYMBOLS: <lt_objects> TYPE ANY TABLE.
ASSIGN lr_data->* TO <lt_objects>.
IF sy-subrc = 0.
LOOP AT <lt_objects> ASSIGNING FIELD-SYMBOL(<ls_object>).
DATA(ls_doc) = VALUE ty_document( ).
ASSIGN COMPONENT 'objectId' OF STRUCTURE <ls_object> TO FIELD-SYMBOL(<lv_id>).
IF sy-subrc = 0.
ls_doc-id = <lv_id>.
ENDIF.
ASSIGN COMPONENT 'name' OF STRUCTURE <ls_object> TO FIELD-SYMBOL(<lv_name>).
IF sy-subrc = 0.
ls_doc-name = <lv_name>.
ENDIF.
APPEND ls_doc TO rt_documents.
ENDLOOP.
ENDIF.
ENDMETHOD.
ENDCLASS.

Datei hochladen (Upload)

Der Upload erfolgt als Multipart-Request mit dem Dateiinhalt:

CLASS zcl_document_handler DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS upload_file
IMPORTING
iv_filename TYPE string
iv_content TYPE xstring
iv_content_type TYPE string
RETURNING
VALUE(rv_document_id) TYPE string
RAISING
cx_web_http_client_error
cx_http_dest_provider_error.
ENDCLASS.
CLASS zcl_document_handler IMPLEMENTATION.
METHOD upload_file.
DATA(lo_dms) = NEW zcl_dms_client( ).
DATA(ls_result) = lo_dms->upload_document(
iv_filename = iv_filename
iv_content_type = iv_content_type
iv_content = iv_content
).
IF ls_result-success = abap_true.
rv_document_id = ls_result-document_id.
ELSE.
" Fehlerbehandlung
RAISE EXCEPTION TYPE cx_web_http_client_error.
ENDIF.
ENDMETHOD.
ENDCLASS.

Upload aus RAP Action

" In der Behavior Implementation
METHOD upload_attachment.
DATA(lo_handler) = NEW zcl_document_handler( ).
TRY.
DATA(lv_doc_id) = lo_handler->upload_file(
iv_filename = keys[ 1 ]-%param-filename
iv_content = keys[ 1 ]-%param-content
iv_content_type = keys[ 1 ]-%param-content_type
).
" Document ID speichern
MODIFY ENTITIES OF zi_document IN LOCAL MODE
ENTITY Document
UPDATE FIELDS ( DmsDocumentId )
WITH VALUE #( (
%tky = keys[ 1 ]-%tky
DmsDocumentId = lv_doc_id
) ).
CATCH cx_web_http_client_error cx_http_dest_provider_error INTO DATA(lx_error).
APPEND VALUE #(
%tky = keys[ 1 ]-%tky
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error
text = lx_error->get_text( ) )
) TO reported-document.
ENDTRY.
ENDMETHOD.

Datei herunterladen (Download)

Der Download gibt den Dateiinhalt als Binary-Stream zurück:

METHOD download_attachment.
DATA(lo_dms) = NEW zcl_dms_client( ).
" Document ID aus Entity lesen
READ ENTITIES OF zi_document IN LOCAL MODE
ENTITY Document
FIELDS ( DmsDocumentId FileName )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_documents).
IF lt_documents IS INITIAL.
RETURN.
ENDIF.
DATA(ls_doc) = lt_documents[ 1 ].
TRY.
DATA(lv_content) = lo_dms->download_document(
iv_document_id = ls_doc-DmsDocumentId
).
" Content an Result übergeben
result = VALUE #( (
%tky = keys[ 1 ]-%tky
%param = VALUE #(
content = lv_content
filename = ls_doc-FileName
content_type = 'application/octet-stream'
)
) ).
CATCH cx_web_http_client_error INTO DATA(lx_error).
APPEND VALUE #(
%tky = keys[ 1 ]-%tky
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error
text = |Download fehlgeschlagen: { lx_error->get_text( ) }| )
) TO reported-document.
ENDTRY.
ENDMETHOD.

Dateien auflisten und suchen

Das Auflisten und Suchen von Dokumenten erfolgt über die List-API:

METHOD list_documents.
DATA(lo_dms) = NEW zcl_dms_client( ).
TRY.
" Alle Dokumente im Root-Ordner
DATA(lt_all_docs) = lo_dms->list_documents( ).
" Mit Suchfilter
DATA(lt_filtered_docs) = lo_dms->list_documents(
iv_search = 'invoice'
).
" In einem bestimmten Ordner
DATA(lt_folder_docs) = lo_dms->list_documents(
iv_folder_id = 'folder-123'
).
" Ergebnis verarbeiten
LOOP AT lt_all_docs INTO DATA(ls_doc).
" Dokument-Metadaten verwenden
DATA(lv_id) = ls_doc-id.
DATA(lv_name) = ls_doc-name.
DATA(lv_size) = ls_doc-size.
ENDLOOP.
CATCH cx_web_http_client_error INTO DATA(lx_error).
" Fehlerbehandlung
ENDTRY.
ENDMETHOD.

Suchfunktion mit Volltextsuche

METHOD search_documents.
DATA(lo_dms) = NEW zcl_dms_client( ).
" CMIS Query für erweiterte Suche
DATA(lv_url) = '/browser/my-repository/root?cmisselector=query&q=' &&
cl_web_http_utility=>escape_url(
|SELECT * FROM cmis:document WHERE CONTAINS('{ iv_search_term }')|
).
TRY.
DATA(lo_request) = mo_http_client->get_http_request( ).
lo_request->set_uri_path( lv_url ).
DATA(lo_response) = mo_http_client->execute( if_web_http_client=>get ).
IF lo_response->get_status( )-code = 200.
" Suchergebnisse parsen
DATA(lv_json) = lo_response->get_text( ).
rt_results = parse_search_results( lv_json ).
ENDIF.
CATCH cx_web_http_client_error INTO DATA(lx_error).
" Fehlerbehandlung
ENDTRY.
ENDMETHOD.

Datei loeschen

Das Loeschen erfolgt ueber die DELETE-Methode:

METHOD delete_attachment.
DATA(lo_dms) = NEW zcl_dms_client( ).
" Document ID aus Entity lesen
READ ENTITIES OF zi_document IN LOCAL MODE
ENTITY Document
FIELDS ( DmsDocumentId )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_documents).
IF lt_documents IS INITIAL.
RETURN.
ENDIF.
TRY.
DATA(lv_success) = lo_dms->delete_document(
iv_document_id = lt_documents[ 1 ]-DmsDocumentId
).
IF lv_success = abap_true.
" DMS ID aus Entity entfernen
MODIFY ENTITIES OF zi_document IN LOCAL MODE
ENTITY Document
UPDATE FIELDS ( DmsDocumentId )
WITH VALUE #( (
%tky = keys[ 1 ]-%tky
DmsDocumentId = ''
) ).
ELSE.
APPEND VALUE #(
%tky = keys[ 1 ]-%tky
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error
text = 'Loeschen fehlgeschlagen' )
) TO reported-document.
ENDIF.
CATCH cx_web_http_client_error INTO DATA(lx_error).
APPEND VALUE #(
%tky = keys[ 1 ]-%tky
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error
text = lx_error->get_text( ) )
) TO reported-document.
ENDTRY.
ENDMETHOD.

Fehlerbehandlung und Retry-Logik

Bei der Kommunikation mit externen Services ist robuste Fehlerbehandlung wichtig:

CLASS zcl_dms_client_robust DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
CONSTANTS:
c_max_retries TYPE i VALUE 3,
c_retry_delay TYPE i VALUE 1000. " Millisekunden
METHODS:
upload_with_retry
IMPORTING
iv_filename TYPE string
iv_content_type TYPE string
iv_content TYPE xstring
RETURNING
VALUE(rs_result) TYPE zcl_dms_client=>ty_upload_result.
PRIVATE SECTION.
DATA: mo_dms_client TYPE REF TO zcl_dms_client.
METHODS:
is_retryable_error
IMPORTING
ix_error TYPE REF TO cx_root
RETURNING
VALUE(rv_retryable) TYPE abap_bool,
wait_before_retry
IMPORTING
iv_attempt TYPE i.
ENDCLASS.
CLASS zcl_dms_client_robust IMPLEMENTATION.
METHOD upload_with_retry.
DATA: lv_attempt TYPE i VALUE 0,
lx_last_error TYPE REF TO cx_root.
" Client erstellen falls noch nicht vorhanden
IF mo_dms_client IS NOT BOUND.
TRY.
mo_dms_client = NEW zcl_dms_client( ).
CATCH cx_http_dest_provider_error INTO DATA(lx_dest_error).
rs_result-success = abap_false.
rs_result-message = lx_dest_error->get_text( ).
RETURN.
ENDTRY.
ENDIF.
" Retry-Loop
WHILE lv_attempt < c_max_retries.
lv_attempt = lv_attempt + 1.
TRY.
rs_result = mo_dms_client->upload_document(
iv_filename = iv_filename
iv_content_type = iv_content_type
iv_content = iv_content
).
IF rs_result-success = abap_true.
RETURN. " Erfolg
ENDIF.
CATCH cx_web_http_client_error INTO DATA(lx_http_error).
lx_last_error = lx_http_error.
IF is_retryable_error( lx_http_error ) AND lv_attempt < c_max_retries.
wait_before_retry( lv_attempt ).
CONTINUE. " Retry
ENDIF.
ENDTRY.
ENDWHILE.
" Alle Versuche fehlgeschlagen
rs_result-success = abap_false.
IF lx_last_error IS BOUND.
rs_result-message = |Upload nach { c_max_retries } Versuchen fehlgeschlagen: { lx_last_error->get_text( ) }|.
ENDIF.
ENDMETHOD.
METHOD is_retryable_error.
" HTTP 5xx Fehler und Timeout sind retryable
rv_retryable = abap_true.
" Spezifische Fehlertypen prüfen
DATA(lx_http_error) = CAST cx_web_http_client_error( ix_error OPTIONAL ).
IF lx_http_error IS BOUND.
" Nur bei temporären Fehlern retry
" 4xx Fehler (Client-Fehler) sind nicht retryable
rv_retryable = abap_true.
ENDIF.
ENDMETHOD.
METHOD wait_before_retry.
" Exponential Backoff: 1s, 2s, 4s
DATA(lv_delay) = c_retry_delay * ( 2 ** ( iv_attempt - 1 ) ).
" In ABAP Cloud: WAIT für kurze Pausen
DATA(lv_end_time) = utclong_current( ) + CONV utclong( lv_delay / 1000 ).
WHILE utclong_current( ) < lv_end_time.
" Warten
ENDWHILE.
ENDMETHOD.
ENDCLASS.

Fehlertypen und Behandlung

HTTP StatusFehlertypAktion
400Bad RequestEingabe prüfen, nicht retry
401UnauthorizedToken refresh, dann retry
403ForbiddenBerechtigungen prüfen
404Not FoundDokument existiert nicht
409ConflictVersionskonflikt, neu laden
429Too Many RequestsBackoff und retry
500Server ErrorRetry mit Backoff
503Service UnavailableRetry mit Backoff

Logging und Monitoring

METHOD log_dms_operation.
" Application Log für DMS-Operationen
DATA(lo_log) = cl_bali_log=>create_with_header(
header = cl_bali_header_setter=>create(
object = 'Z_DMS'
subobject = 'OPERATIONS'
external_id = iv_document_id
)
).
CASE iv_operation.
WHEN 'UPLOAD'.
lo_log->add_item( cl_bali_message_setter=>create(
severity = if_bali_constants=>c_category_free_text
text = |Dokument hochgeladen: { iv_filename }|
) ).
WHEN 'DOWNLOAD'.
lo_log->add_item( cl_bali_message_setter=>create(
severity = if_bali_constants=>c_category_free_text
text = |Dokument heruntergeladen: { iv_document_id }|
) ).
WHEN 'DELETE'.
lo_log->add_item( cl_bali_message_setter=>create(
severity = if_bali_constants=>c_category_free_text
text = |Dokument geloescht: { iv_document_id }|
) ).
WHEN 'ERROR'.
lo_log->add_item( cl_bali_message_setter=>create(
severity = if_bali_constants=>c_category_error
text = |Fehler: { iv_error_message }|
) ).
ENDCASE.
cl_bali_log_db=>get_instance( )->save_log( log = lo_log ).
ENDMETHOD.

Best Practices

1. Content-Type korrekt setzen

" Content-Type aus Dateiendung ermitteln
METHOD get_content_type.
CASE to_lower( iv_extension ).
WHEN 'pdf'.
rv_content_type = 'application/pdf'.
WHEN 'docx'.
rv_content_type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'.
WHEN 'xlsx'.
rv_content_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'.
WHEN 'png'.
rv_content_type = 'image/png'.
WHEN 'jpg' OR 'jpeg'.
rv_content_type = 'image/jpeg'.
WHEN OTHERS.
rv_content_type = 'application/octet-stream'.
ENDCASE.
ENDMETHOD.

2. Grosse Dateien chunked uebertragen

" Fuer grosse Dateien: Chunked Upload
CONSTANTS: c_chunk_size TYPE i VALUE 5242880. " 5 MB
METHOD upload_large_file.
DATA: lv_offset TYPE i VALUE 0,
lv_chunk TYPE xstring.
WHILE lv_offset < xstrlen( iv_content ).
lv_chunk = iv_content+lv_offset(c_chunk_size).
" Chunk hochladen
upload_chunk(
iv_session_id = iv_session_id
iv_offset = lv_offset
iv_chunk = lv_chunk
).
lv_offset = lv_offset + c_chunk_size.
ENDWHILE.
ENDMETHOD.

3. Metadaten strukturiert speichern

" Metadaten als JSON im DMS speichern
DATA(ls_metadata) = VALUE ty_document_metadata(
created_by = sy-uname
created_at = utclong_current( )
business_object = 'SALESORDER'
business_key = '0000001234'
document_type = 'INVOICE'
).
DATA(lv_metadata_json) = /ui2/cl_json=>serialize( data = ls_metadata ).

Verwandte Themen

Fazit

Der SAP Document Management Service bietet eine robuste, cloud-native Lösung für Dokumentenverwaltung in ABAP Cloud. Mit der richtigen Architektur – Communication Arrangements, HTTP-Client und strukturierter Fehlerbehandlung – lässt sich DMS nahtlos in RAP-Anwendungen integrieren.