Intégration du SAP Document Management Service dans ABAP Cloud

Catégorie
Integration
Publié
Auteur
Johannes

Le SAP Document Management Service (DMS) est un service BTP pour la gestion centralisée de documents et de fichiers. Il offre une API REST pour le téléversement, le téléchargement, la gestion des versions et des métadonnées – idéal pour les applications RAP qui doivent gérer des pièces jointes.

Architecture et concept

Le Document Management Service fonctionne comme un référentiel central de documents dans la SAP Business Technology Platform :

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

Avantages du DMS

AspectDescription
Stockage centralTous les documents au même endroit
VersionnementContrôle automatique des versions
MétadonnéesPropriétés de documents structurées
RechercheRecherche plein texte dans les documents
SécuritéContrôle d’accès et piste d’audit
ÉvolutivitéArchitecture cloud-native

Configuration et paramétrage

1. Activer le service DMS dans BTP

Dans le cockpit BTP, le Document Management Service doit être activé et un référentiel créé :

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

2. Créer un Communication Arrangement

Dans ABAP Cloud, la connexion est configurée via un Communication Arrangement :

" Communication Scenario pour DMS
" Créé dans ADT sous :
" Source Library → Communication Scenarios

Définition du Communication Scenario (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 dans le cockpit BTP

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

4. Communication Arrangement dans ABAP

" Gérer le Communication Arrangement dans ADT ou 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

Classe client DMS

Une classe centrale encapsule la communication avec le DMS :

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.
" Récupérer la destination HTTP depuis le Communication Arrangement
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
).
" URL de base pour l'API DMS
mv_base_url = '/browser/' && mv_repository && '/root'.
ENDMETHOD.
METHOD upload_document.
DATA: lv_url TYPE string.
" Créer l'URL pour le téléversement
IF iv_folder_id IS NOT INITIAL.
lv_url = mv_base_url && '/' && iv_folder_id.
ELSE.
lv_url = mv_base_url.
ENDIF.
" Préparer la requête
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"
).
" Créer le corps multipart
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 }|.
" Ajouter le contenu en binaire
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 }|
).
" Envoyer la requête
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.
" Extraire l'ID du document de la réponse
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 = 'Téléversement réussi'.
ELSE.
rs_result-success = abap_false.
rs_result-message = |Échec du téléversement : 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.
" Analyser la réponse JSON
" Exemple : {"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
).
" Assignation des champs
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.
" Analyser le tableau JSON
DATA: lr_data TYPE REF TO data.
/ui2/cl_json=>deserialize(
EXPORTING
json = iv_json
CHANGING
data = lr_data
).
" Parcourir le tableau et extraire les documents
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.

Téléverser un fichier (Upload)

Le téléversement s’effectue en tant que requête multipart avec le contenu du fichier :

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.
" Gestion des erreurs
RAISE EXCEPTION TYPE cx_web_http_client_error.
ENDIF.
ENDMETHOD.
ENDCLASS.

Téléversement depuis une action RAP

" Dans l'implémentation du Behavior
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
).
" Sauvegarder l'ID du document
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.

Télécharger un fichier (Download)

Le téléchargement retourne le contenu du fichier sous forme de flux binaire :

METHOD download_attachment.
DATA(lo_dms) = NEW zcl_dms_client( ).
" Lire l'ID du document depuis l'entité
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
).
" Passer le contenu au résultat
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 = |Échec du téléchargement : { lx_error->get_text( ) }| )
) TO reported-document.
ENDTRY.
ENDMETHOD.

Lister et rechercher des fichiers

Le listage et la recherche de documents s’effectuent via l’API de liste :

METHOD list_documents.
DATA(lo_dms) = NEW zcl_dms_client( ).
TRY.
" Tous les documents dans le dossier racine
DATA(lt_all_docs) = lo_dms->list_documents( ).
" Avec filtre de recherche
DATA(lt_filtered_docs) = lo_dms->list_documents(
iv_search = 'invoice"
).
" Dans un dossier spécifique
DATA(lt_folder_docs) = lo_dms->list_documents(
iv_folder_id = 'folder-123"
).
" Traiter les résultats
LOOP AT lt_all_docs INTO DATA(ls_doc).
" Utiliser les métadonnées du document
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).
" Gestion des erreurs
ENDTRY.
ENDMETHOD.

Fonction de recherche avec recherche plein texte

METHOD search_documents.
DATA(lo_dms) = NEW zcl_dms_client( ).
" Requête CMIS pour recherche avancée
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.
" Analyser les résultats de recherche
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).
" Gestion des erreurs
ENDTRY.
ENDMETHOD.

Supprimer un fichier

La suppression s’effectue via la méthode DELETE :

METHOD delete_attachment.
DATA(lo_dms) = NEW zcl_dms_client( ).
" Lire l'ID du document depuis l'entité
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.
" Supprimer l'ID DMS de l'entité
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 = 'Échec de la suppression' )
) 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.

Gestion des erreurs et logique de réessai

Lors de la communication avec des services externes, une gestion robuste des erreurs est importante :

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. " Millisecondes
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.
" Créer le client si pas encore instancié
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.
" Boucle de réessai
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. " Succès
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. " Réessayer
ENDIF.
ENDTRY.
ENDWHILE.
" Toutes les tentatives ont échoué
rs_result-success = abap_false.
IF lx_last_error IS BOUND.
rs_result-message = |Téléversement échoué après { c_max_retries } tentatives : { lx_last_error->get_text( ) }|.
ENDIF.
ENDMETHOD.
METHOD is_retryable_error.
" Les erreurs HTTP 5xx et timeout sont réessayables
rv_retryable = abap_true.
" Vérifier les types d'erreurs spécifiques
DATA(lx_http_error) = CAST cx_web_http_client_error( ix_error OPTIONAL ).
IF lx_http_error IS BOUND.
" Réessayer uniquement pour les erreurs temporaires
" Les erreurs 4xx (erreurs client) ne sont pas réessayables
rv_retryable = abap_true.
ENDIF.
ENDMETHOD.
METHOD wait_before_retry.
" Backoff exponentiel : 1s, 2s, 4s
DATA(lv_delay) = c_retry_delay * ( 2 ** ( iv_attempt - 1 ) ).
" Dans ABAP Cloud : WAIT pour des pauses courtes
DATA(lv_end_time) = utclong_current( ) + CONV utclong( lv_delay / 1000 ).
WHILE utclong_current( ) < lv_end_time.
" Attendre
ENDWHILE.
ENDMETHOD.
ENDCLASS.

Types d’erreurs et gestion

Statut HTTPType d’erreurAction
400Bad RequestVérifier l’entrée, pas de réessai
401UnauthorizedRafraîchir le token, puis réessayer
403ForbiddenVérifier les autorisations
404Not FoundLe document n’existe pas
409ConflictConflit de version, recharger
429Too Many RequestsBackoff et réessai
500Server ErrorRéessai avec backoff
503Service UnavailableRéessai avec backoff

Journalisation et monitoring

METHOD log_dms_operation.
" Log d'application pour les opérations DMS
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 = |Document téléversé : { iv_filename }|
) ).
WHEN 'DOWNLOAD'.
lo_log->add_item( cl_bali_message_setter=>create(
severity = if_bali_constants=>c_category_free_text
text = |Document téléchargé : { iv_document_id }|
) ).
WHEN 'DELETE'.
lo_log->add_item( cl_bali_message_setter=>create(
severity = if_bali_constants=>c_category_free_text
text = |Document supprimé : { iv_document_id }|
) ).
WHEN 'ERROR'.
lo_log->add_item( cl_bali_message_setter=>create(
severity = if_bali_constants=>c_category_error
text = |Erreur : { iv_error_message }|
) ).
ENDCASE.
cl_bali_log_db=>get_instance( )->save_log( log = lo_log ).
ENDMETHOD.

Bonnes pratiques

1. Définir correctement le Content-Type

" Déterminer le Content-Type à partir de l'extension de fichier
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. Transférer les gros fichiers par morceaux

" Pour les gros fichiers : téléversement par morceaux
CONSTANTS: c_chunk_size TYPE i VALUE 5242880. " 5 Mo
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).
" Téléverser le morceau
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. Stocker les métadonnées de manière structurée

" Stocker les métadonnées en JSON dans le DMS
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 ).

Sujets connexes

Conclusion

Le SAP Document Management Service offre une solution robuste et cloud-native pour la gestion de documents dans ABAP Cloud. Avec la bonne architecture – Communication Arrangements, client HTTP et gestion structurée des erreurs – le DMS peut être intégré de manière transparente dans les applications RAP.