Gestion des pieces jointes et des documents dans ABAP Cloud

Catégorie
RAP
Publié
Auteur
Johannes

La gestion des pieces jointes est un sujet central dans les applications metier modernes. Qu’il s’agisse de factures, de contrats ou de dessins techniques, les utilisateurs s’attendent a pouvoir telecharger, stocker et retelecharger des documents directement dans l’application. Dans ABAP Cloud et RAP, il existe plusieurs approches pour repondre a cette exigence.

Concepts fondamentaux

Dans ABAP Cloud, differentes methodes de gestion des pieces jointes sont disponibles :

ApprocheDescriptionCas d’utilisation
Champs LOB dans CDSLarge Objects directement dans la base de donneesFichiers de petite a moyenne taille
Attachment Service (GOS)Service standard SAP pour les pieces jointesIntegration avec les objets standard SAP
External StorageServices de stockage externes (Object Store)Grands fichiers, conformite
Implementation personnaliseeTables et logique propresFlexibilite maximale

Vue d’ensemble de l’architecture

+-----------------------------------------------------------------------------+
| Gestion des pieces jointes dans RAP |
| |
| +------------------------------------------------------------------------+ |
| | Interface Fiori Elements | |
| | +---------------------+ +---------------------+ | |
| | | Controle Upload | | Action Download | | |
| | | (Drag & Drop) | | (Bouton/Lien) | | |
| | +----------+----------+ +----------+----------+ | |
| +-------------|-----------------------|----------------------------------+ |
| | | |
| v v |
| +------------------------------------------------------------------------+ |
| | RAP Business Object | |
| | +---------------------+ +---------------------+ | |
| | | Champ LOB | | RAP Actions | | |
| | | (Attachment) | | upload/download | | |
| | +----------+----------+ +----------+----------+ | |
| +-------------|-----------------------|----------------------------------+ |
| | | |
| v v |
| +------------------------------------------------------------------------+ |
| | Options de stockage | |
| | +-------------------+ +-------------------+ +-------------------+ | |
| | | HANA LOB | | Object Store | | DMS | | |
| | | (Base donnees) | | (Cloud) | | (Optionnel) | | |
| | +-------------------+ +-------------------+ +-------------------+ | |
| +------------------------------------------------------------------------+ |
+-----------------------------------------------------------------------------+

Gestion LOB dans les vues CDS

L’approche la plus simple pour les pieces jointes est l’utilisation de champs Large Object (LOB) directement dans la table de base de donnees. Les vues CDS prennent en charge les champs LOB pour les donnees binaires.

Table de base de donnees avec champ LOB

@EndUserText.label : 'Document avec piece jointe"
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zdocument {
key client : abap.clnt not null;
key document_uuid : sysuuid_x16 not null;
document_id : abap.numc(10);
document_name : abap.char(100);
description : abap.char(255);
// Champs LOB pour la piece jointe
@AbapCatalog.typeUsage : #OPEN
attachment : abap.rawstring(0);
mime_type : abap.char(128);
file_name : abap.char(255);
file_size : abap.int4;
created_by : abap.uname;
created_at : timestampl;
last_changed_by : abap.uname;
last_changed_at : timestampl;
}

Remarques importantes sur les champs LOB :

  • abap.rawstring(0) definit un BLOB (Binary Large Object) sans limitation de longueur
  • abap.string(0) serait un CLOB pour les donnees textuelles
  • Les champs LOB sont stockes efficacement dans HANA (pas dans le Row Store)
  • Taille maximale : jusqu’a 2 Go par champ

Vue CDS avec champ LOB

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Document avec piece jointe"
define root view entity ZI_Document
as select from zdocument
{
key document_uuid as DocumentUUID,
document_id as DocumentID,
document_name as DocumentName,
description as Description,
// Exposer le champ LOB
@Semantics.largeObject: {
mimeType: 'MimeType',
fileName: 'FileName',
contentDispositionPreference: #ATTACHMENT
}
attachment as Attachment,
mime_type as MimeType,
file_name as FileName,
file_size as FileSize,
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true
created_at as CreatedAt,
@Semantics.user.lastChangedBy: true
last_changed_by as LastChangedBy,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt
}

L’annotation @Semantics.largeObject est cruciale :

ParametreDescription
mimeTypeReference au champ avec le type MIME (par ex. application/pdf)
fileNameReference au champ avec le nom de fichier
contentDispositionPreference#ATTACHMENT pour le telechargement, #INLINE pour l’affichage

Upload et Download dans Fiori Elements

Fiori Elements prend automatiquement en charge les champs LOB avec une fonctionnalite d’upload et de download.

Vue de projection avec annotations UI

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Projection Document"
@Metadata.allowExtensions: true
define root view entity ZC_Document
provider contract transactional_query
as projection on ZI_Document
{
key DocumentUUID,
DocumentID,
DocumentName,
Description,
// Champ LOB pour l'UI
Attachment,
MimeType,
FileName,
FileSize,
CreatedBy,
CreatedAt,
LastChangedBy,
LastChangedAt
}

Extension de metadonnees pour l’UI d’upload

@Metadata.layer: #CORE
annotate view ZC_Document with
{
@UI.facet: [
{
id: 'GeneralInfo',
type: #IDENTIFICATION_REFERENCE,
label: 'Informations generales',
position: 10
},
{
id: 'AttachmentFacet',
type: #FIELDGROUP_REFERENCE,
targetQualifier: 'Attachment',
label: 'Piece jointe',
position: 20
}
]
@UI.lineItem: [{ position: 10, importance: #HIGH }]
@UI.identification: [{ position: 10 }]
DocumentID;
@UI.lineItem: [{ position: 20, importance: #HIGH }]
@UI.identification: [{ position: 20 }]
DocumentName;
@UI.identification: [{ position: 30 }]
Description;
// Champs de piece jointe
@UI.fieldGroup: [{ qualifier: 'Attachment', position: 10, label: 'Fichier' }]
Attachment;
@UI.fieldGroup: [{ qualifier: 'Attachment', position: 20, label: 'Type de fichier' }]
@UI.hidden: true
MimeType;
@UI.fieldGroup: [{ qualifier: 'Attachment', position: 30, label: 'Nom du fichier' }]
FileName;
@UI.fieldGroup: [{ qualifier: 'Attachment', position: 40, label: 'Taille (octets)' }]
FileSize;
}

Definition de comportement pour la gestion LOB

managed implementation in class zbp_i_document unique;
strict ( 2 );
define behavior for ZI_Document alias Document
persistent table zdocument
lock master
authorization master ( instance )
{
field ( readonly ) DocumentUUID, CreatedBy, CreatedAt, LastChangedBy, LastChangedAt;
field ( readonly ) FileSize;
field ( mandatory ) DocumentName;
create;
update;
delete;
// Determination pour le calcul de FileSize
determination calculateFileSize on modify { field Attachment; }
mapping for zdocument
{
DocumentUUID = document_uuid;
DocumentID = document_id;
DocumentName = document_name;
Description = description;
Attachment = attachment;
MimeType = mime_type;
FileName = file_name;
FileSize = file_size;
CreatedBy = created_by;
CreatedAt = created_at;
LastChangedBy = last_changed_by;
LastChangedAt = last_changed_at;
}
}

Implementation du comportement

CLASS lhc_document DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS calculatefilesize FOR DETERMINE ON MODIFY
IMPORTING keys FOR document~calculatefilesize.
ENDCLASS.
CLASS lhc_document IMPLEMENTATION.
METHOD calculatefilesize.
" Lire les documents avec les pieces jointes modifiees
READ ENTITIES OF zi_document IN LOCAL MODE
ENTITY document
FIELDS ( attachment )
WITH CORRESPONDING #( keys )
RESULT DATA(documents).
" Calculer FileSize pour chaque document
LOOP AT documents ASSIGNING FIELD-SYMBOL(<doc>).
DATA(lv_size) = xstrlen( <doc>-attachment ).
MODIFY ENTITIES OF zi_document IN LOCAL MODE
ENTITY document
UPDATE FIELDS ( filesize )
WITH VALUE #( ( %tky = <doc>-%tky
filesize = lv_size ) )
FAILED DATA(failed)
REPORTED DATA(reported).
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Actions personnalisees Upload/Download

Pour plus de controle sur le processus d’upload et de download, des actions RAP personnalisees peuvent etre implementees.

Definition de comportement avec Actions

managed implementation in class zbp_i_document unique;
strict ( 2 );
define behavior for ZI_Document alias Document
persistent table zdocument
lock master
authorization master ( instance )
{
// ... Operations standard ...
// Action Upload avec parametre
action uploadAttachment parameter ZD_AttachmentUpload result [1] $self;
// Action Download
action downloadAttachment result [1] ZD_AttachmentDownload;
// Supprimer la piece jointe
action deleteAttachment result [1] $self;
}

Entite abstraite pour les parametres d’upload

@EndUserText.label: 'Parametre Upload Piece jointe"
define abstract entity ZD_AttachmentUpload
{
@Semantics.largeObject: {
mimeType: 'MimeType',
fileName: 'FileName',
contentDispositionPreference: #ATTACHMENT
}
Content : abap.rawstring(0);
MimeType : abap.char(128);
FileName : abap.char(255);
}

Entite abstraite pour le resultat de download

@EndUserText.label: 'Resultat Download Piece jointe"
define abstract entity ZD_AttachmentDownload
{
@Semantics.largeObject: {
mimeType: 'MimeType',
fileName: 'FileName',
contentDispositionPreference: #ATTACHMENT
}
Content : abap.rawstring(0);
MimeType : abap.char(128);
FileName : abap.char(255);
FileSize : abap.int4;
}

Implementation des actions

CLASS lhc_document DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS uploadattachment FOR MODIFY
IMPORTING keys FOR ACTION document~uploadattachment RESULT result.
METHODS downloadattachment FOR READ
IMPORTING keys FOR ACTION document~downloadattachment RESULT result.
METHODS deleteattachment FOR MODIFY
IMPORTING keys FOR ACTION document~deleteattachment RESULT result.
ENDCLASS.
CLASS lhc_document IMPLEMENTATION.
METHOD uploadattachment.
" Validation
LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>).
IF <key>-%param-content IS INITIAL.
APPEND VALUE #( %tky = <key>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Veuillez selectionner un fichier' ) )
TO reported-document.
APPEND VALUE #( %tky = <key>-%tky ) TO failed-document.
CONTINUE.
ENDIF.
" Verifier la taille du fichier (max 10 Mo)
DATA(lv_size) = xstrlen( <key>-%param-content ).
IF lv_size > 10485760. " 10 Mo
APPEND VALUE #( %tky = <key>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Fichier trop volumineux (max. 10 Mo)' ) )
TO reported-document.
APPEND VALUE #( %tky = <key>-%tky ) TO failed-document.
CONTINUE.
ENDIF.
" Valider le type MIME
DATA(lv_allowed) = abap_false.
CASE <key>-%param-mimetype.
WHEN 'application/pdf"
OR 'image/jpeg"
OR 'image/png"
OR 'application/vnd.openxmlformats-officedocument.wordprocessingml.document"
OR 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'.
lv_allowed = abap_true.
ENDCASE.
IF lv_allowed = abap_false.
APPEND VALUE #( %tky = <key>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Type de fichier non autorise' ) )
TO reported-document.
APPEND VALUE #( %tky = <key>-%tky ) TO failed-document.
CONTINUE.
ENDIF.
" Sauvegarder la piece jointe
MODIFY ENTITIES OF zi_document IN LOCAL MODE
ENTITY document
UPDATE FIELDS ( attachment mimetype filename filesize )
WITH VALUE #( ( %tky = <key>-%tky
attachment = <key>-%param-content
mimetype = <key>-%param-mimetype
filename = <key>-%param-filename
filesize = lv_size ) )
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
IF update_failed IS NOT INITIAL.
APPEND LINES OF update_failed-document TO failed-document.
APPEND LINES OF update_reported-document TO reported-document.
ELSE.
" Succes : Retourner l'entite modifiee
READ ENTITIES OF zi_document IN LOCAL MODE
ENTITY document
ALL FIELDS
WITH VALUE #( ( %tky = <key>-%tky ) )
RESULT DATA(documents).
result = VALUE #( FOR doc IN documents
( %tky = doc-%tky
%param = doc ) ).
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD downloadattachment.
" Lire le document avec la piece jointe
READ ENTITIES OF zi_document IN LOCAL MODE
ENTITY document
FIELDS ( attachment mimetype filename filesize )
WITH CORRESPONDING #( keys )
RESULT DATA(documents)
FAILED failed.
" Construire le resultat de download
LOOP AT documents ASSIGNING FIELD-SYMBOL(<doc>).
IF <doc>-attachment IS INITIAL.
APPEND VALUE #( %tky = <doc>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-warning
text = 'Aucune piece jointe disponible' ) )
TO reported-document.
CONTINUE.
ENDIF.
APPEND VALUE #( %tky = <doc>-%tky
%param = VALUE #( content = <doc>-attachment
mimetype = <doc>-mimetype
filename = <doc>-filename
filesize = <doc>-filesize ) )
TO result.
ENDLOOP.
ENDMETHOD.
METHOD deleteattachment.
" Vider les champs de piece jointe
MODIFY ENTITIES OF zi_document IN LOCAL MODE
ENTITY document
UPDATE FIELDS ( attachment mimetype filename filesize )
WITH VALUE #( FOR key IN keys
( %tky = key-%tky
attachment = VALUE #( )
mimetype = '"
filename = '"
filesize = 0 ) )
FAILED failed
REPORTED reported.
IF failed IS INITIAL.
" Retourner les entites modifiees
READ ENTITIES OF zi_document IN LOCAL MODE
ENTITY document
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(documents).
result = VALUE #( FOR doc IN documents
( %tky = doc-%tky
%param = doc ) ).
ENDIF.
ENDMETHOD.
ENDCLASS.

Plusieurs pieces jointes par entite

Si un Business Object doit prendre en charge plusieurs pieces jointes, une entite enfant separee pour les pieces jointes est utilisee.

Entite enfant de piece jointe

@EndUserText.label : 'Pieces jointes du document"
define table zdoc_attachment {
key client : abap.clnt not null;
key attachment_uuid : sysuuid_x16 not null;
parent_uuid : sysuuid_x16 not null;
attachment_name : abap.char(100);
@AbapCatalog.typeUsage : #OPEN
content : abap.rawstring(0);
mime_type : abap.char(128);
file_name : abap.char(255);
file_size : abap.int4;
created_by : abap.uname;
created_at : timestampl;
}

Vue CDS avec composition

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Document"
define root view entity ZI_DocumentWithAtt
as select from zdocument
composition [0..*] of ZI_DocumentAttachment as _Attachments
{
key document_uuid as DocumentUUID,
document_id as DocumentID,
document_name as DocumentName,
description as Description,
// Association aux pieces jointes
_Attachments
}
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Piece jointe du document"
define view entity ZI_DocumentAttachment
as select from zdoc_attachment
association to parent ZI_DocumentWithAtt as _Document
on $projection.ParentUUID = _Document.DocumentUUID
{
key attachment_uuid as AttachmentUUID,
parent_uuid as ParentUUID,
attachment_name as AttachmentName,
@Semantics.largeObject: {
mimeType: 'MimeType',
fileName: 'FileName',
contentDispositionPreference: #ATTACHMENT
}
content as Content,
mime_type as MimeType,
file_name as FileName,
file_size as FileSize,
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true
created_at as CreatedAt,
_Document
}

Definition de comportement pour Parent-Enfant

managed implementation in class zbp_i_documentwithatt unique;
strict ( 2 );
define behavior for ZI_DocumentWithAtt alias Document
persistent table zdocument
lock master
authorization master ( instance )
{
create;
update;
delete;
association _Attachments { create; }
mapping for zdocument
{
DocumentUUID = document_uuid;
DocumentID = document_id;
DocumentName = document_name;
Description = description;
}
}
define behavior for ZI_DocumentAttachment alias Attachment
persistent table zdoc_attachment
lock dependent by _Document
authorization dependent by _Document
{
field ( readonly ) AttachmentUUID, ParentUUID, CreatedBy, CreatedAt;
field ( readonly ) FileSize;
update;
delete;
determination calculateSize on modify { field Content; }
mapping for zdoc_attachment
{
AttachmentUUID = attachment_uuid;
ParentUUID = parent_uuid;
AttachmentName = attachment_name;
Content = content;
MimeType = mime_type;
FileName = file_name;
FileSize = file_size;
CreatedBy = created_by;
CreatedAt = created_at;
}
}

Options de stockage et limites

Limites de base de donnees

Base de donneesTaille max. LOBRecommandation
SAP HANA2 GoJusqu’a 100 Mo directement en BD
MaxDB2 GoJusqu’a 100 Mo directement en BD

Alternatives pour les gros fichiers

Pour les fichiers de plus de 100 Mo ou en cas de besoin de stockage eleve, des solutions de stockage externes sont recommandees :

  1. SAP Object Store Service sur BTP
  2. Azure Blob Storage / AWS S3 via Destination
  3. SAP Document Management Service

Integration avec Object Store

CLASS zcl_object_store_client DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_object_metadata,
object_key TYPE string,
bucket TYPE string,
content_type TYPE string,
size TYPE i,
END OF ty_object_metadata.
METHODS upload_object
IMPORTING
iv_bucket TYPE string
iv_object_key TYPE string
iv_content TYPE xstring
iv_content_type TYPE string
RETURNING
VALUE(rs_metadata) TYPE ty_object_metadata
RAISING
cx_http_dest_provider_error
cx_web_http_client_error.
METHODS download_object
IMPORTING
iv_bucket TYPE string
iv_object_key TYPE string
RETURNING
VALUE(rv_content) TYPE xstring
RAISING
cx_http_dest_provider_error
cx_web_http_client_error.
METHODS delete_object
IMPORTING
iv_bucket TYPE string
iv_object_key TYPE string
RAISING
cx_http_dest_provider_error
cx_web_http_client_error.
ENDCLASS.
CLASS zcl_object_store_client IMPLEMENTATION.
METHOD upload_object.
" Destination HTTP pour Object Store
DATA(lo_dest) = cl_http_destination_provider=>create_by_cloud_destination(
i_name = 'OBJECT_STORE"
i_service_instance_name = 'object-store-instance' ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_dest ).
" Preparer la requete
DATA(lo_request) = lo_client->get_http_request( ).
lo_request->set_uri_path( |/{ iv_bucket }/{ iv_object_key }| ).
lo_request->set_header_field( i_name = 'Content-Type"
i_value = iv_content_type ).
lo_request->set_binary_body( iv_content ).
" Executer l'upload (PUT)
DATA(lo_response) = lo_client->execute( if_web_http_client=>put ).
IF lo_response->get_status( )-code <> 200
AND lo_response->get_status( )-code <> 201.
RAISE EXCEPTION TYPE cx_web_http_client_error.
ENDIF.
" Retourner les metadonnees
rs_metadata = VALUE #( object_key = iv_object_key
bucket = iv_bucket
content_type = iv_content_type
size = xstrlen( iv_content ) ).
lo_client->close( ).
ENDMETHOD.
METHOD download_object.
DATA(lo_dest) = cl_http_destination_provider=>create_by_cloud_destination(
i_name = 'OBJECT_STORE"
i_service_instance_name = 'object-store-instance' ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_dest ).
DATA(lo_request) = lo_client->get_http_request( ).
lo_request->set_uri_path( |/{ iv_bucket }/{ iv_object_key }| ).
" Executer le download (GET)
DATA(lo_response) = lo_client->execute( if_web_http_client=>get ).
IF lo_response->get_status( )-code <> 200.
RAISE EXCEPTION TYPE cx_web_http_client_error.
ENDIF.
rv_content = lo_response->get_binary( ).
lo_client->close( ).
ENDMETHOD.
METHOD delete_object.
DATA(lo_dest) = cl_http_destination_provider=>create_by_cloud_destination(
i_name = 'OBJECT_STORE"
i_service_instance_name = 'object-store-instance' ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_dest ).
DATA(lo_request) = lo_client->get_http_request( ).
lo_request->set_uri_path( |/{ iv_bucket }/{ iv_object_key }| ).
" Executer la suppression
DATA(lo_response) = lo_client->execute( if_web_http_client=>delete ).
IF lo_response->get_status( )-code <> 200
AND lo_response->get_status( )-code <> 204.
RAISE EXCEPTION TYPE cx_web_http_client_error.
ENDIF.
lo_client->close( ).
ENDMETHOD.
ENDCLASS.

Bonnes pratiques

1. Validation a l’upload

METHOD validate_attachment.
" Verifier la taille du fichier
DATA(lv_max_size) = 10485760. " 10 Mo
IF xstrlen( iv_content ) > lv_max_size.
RAISE EXCEPTION TYPE zcx_attachment_error
EXPORTING
textid = zcx_attachment_error=>file_too_large
max_size = lv_max_size.
ENDIF.
" Verifier le type MIME
DATA(lt_allowed_types) = VALUE string_table(
( `application/pdf` )
( `image/jpeg` )
( `image/png` )
( `application/vnd.openxmlformats-officedocument.wordprocessingml.document` )
( `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` )
).
IF NOT line_exists( lt_allowed_types[ table_line = iv_mime_type ] ).
RAISE EXCEPTION TYPE zcx_attachment_error
EXPORTING
textid = zcx_attachment_error=>mime_type_not_allowed
mime_type = iv_mime_type.
ENDIF.
" Analyse antivirus (si disponible)
" cl_vscan_scanner=>scan( ... )
ENDMETHOD.

2. Optimisation des performances

" Charger le contenu de la piece jointe uniquement si necessaire
READ ENTITIES OF zi_document IN LOCAL MODE
ENTITY document
FIELDS ( documentname mimetype filename filesize ) " PAS : attachment
WITH CORRESPONDING #( keys )
RESULT DATA(document_list).
" Charger la piece jointe separement pour le download
READ ENTITIES OF zi_document IN LOCAL MODE
ENTITY document
FIELDS ( attachment )
WITH CORRESPONDING #( download_keys )
RESULT DATA(attachments).

3. Securite

  • Verifier les autorisations : Download de piece jointe uniquement pour les utilisateurs autorises
  • Analyse antivirus : Scanner les fichiers avant le stockage pour les applications critiques
  • Validation des entrees : Verifier strictement les types et tailles de fichiers
  • Chiffrement : Stocker les documents sensibles de maniere chiffree

Resume

La gestion des pieces jointes dans ABAP Cloud offre des options flexibles :

ApprocheAvantagesInconvenients
LOB dans la tableSimple, UI automatiqueTaille limitee, charge BD
Actions personnaliseesControle total, validationPlus d’effort d’implementation
Entite enfantPlusieurs pieces jointesStructure plus complexe
Object StoreTaille illimitee, performantDependance externe

Le choix depend des exigences : pour les documents simples jusqu’a 10 Mo, les champs LOB suffisent, pour les scenarios complexes avec beaucoup de gros fichiers, une combinaison de metadonnees en BD et de contenu dans l’Object Store est recommandee.

Sujets connexes