RAP - File Upload (Stream)
How can you easily load files into your RAP object and make them available in ABAP? Let's take a look at the details.
Table of contents
In this article we will look at one way of uploading files, and we will discuss the different ways of using it.
Introduction
Uploading and downloading files is now standard in applications. Therefore, in the ABAP RESTful Programming Model we also need a simple solution to do this. Today we will look at uploading, i.e. storing a file in our application. To do this, we will extend the Report Pattern application with the additional function.
Extension
In this section, we will extend the application with two uploads to load an Excel file and an image into the application.
Table
In the first step, we need to extend the table. To upload files, we need three fields with a certain length.
attachment : abap.rawstring(0);
mimetype : abap.char(128);
filename : abap.char(128);
The attachment is the file in binary form, the mimetype describes the type of the file and the filename is the name of the file after uploading. The length of the individual fields is fixed. In the first step we extend the table ZBS_DRP_ADDCURR by the 6 fields.
define table zbs_drp_addcurr {
key client : abap.clnt not null;
key currency : waers not null;
ccomment : abap.char(60);
documentation : abap.string(0);
picture_url : abap.string(0);
last_editor : abap.char(12);
excel_attachment : abap.rawstring(0);
excel_mimetype : abap.char(128);
excel_filename : abap.char(128);
picture_attachment : abap.rawstring(0);
picture_mimetype : abap.char(128);
picture_filename : abap.char(128);
local_last_changed : abp_locinst_lastchange_tstmpl;
last_changed : abp_lastchange_tstmpl;
}
In the second step, we extend our draft table ZBS_DRP_CURRD so that our application continues to work. We remove the underscores because we are still normalizing the fields in the Core Data Service.
define table zbs_drp_currd {
key client : abap.clnt not null;
key currency : waers not null;
decimals : abap.int1;
currencyisocode : abap.char(3);
alternativecurrencykey : abap.char(3);
currencyname : abap.char(40);
currencyshortname : abap.char(15);
currencycomment : abap.char(60);
documentation : abap.string(0);
pictureurl : abap.string(0);
lasteditor : abap.char(12);
excelattachment : abap.rawstring(0);
excelmimetype : abap.char(128);
excelfilename : abap.char(128);
pictureattachment : abap.rawstring(0);
picturemimetype : abap.char(128);
picturefilename : abap.char(128);
locallastchanged : abp_locinst_lastchange_tstmpl;
lastchanged : abp_lastchange_tstmpl;
"%admin" : include sych_bdl_draft_admin_inc;
}
Basis
Since we use a basic view "ZBS_B_DRPAdditionalCurrency" to normalize the fields in the data model, we also add the fields here and rename them. This means that they also match the field names in the draft table.
define view entity ZBS_B_DRPAdditionalCurrency
as select from zbs_drp_addcurr
{
key currency as Currency,
ccomment as CurrencyComment,
documentation as Documentation,
picture_url as PictureURL,
last_editor as LastEditor,
excel_attachment as ExcelAttachement,
excel_mimetype as ExcelMimetype,
excel_filename as ExcelFilename,
picture_attachment as PictureAttachement,
picture_mimetype as PictureMimetype,
picture_filename as PictureFilename,
last_changed as LastChanged,
local_last_changed as LocalLastChanged
}
Interface
In the root view of the object, we also add the fields accordingly so that they are then available in the RAP object.
define root view entity ZBS_R_DRPCurrency
as select from I_Currency
composition of many ZBS_I_DRPCurrencyCountry as _Country
association of one to one ZBS_B_DRPAdditionalCurrency as _Data on _Data.Currency = $projection.Currency
association of one to one I_BusinessUserVH as _User on _User.UserID = $projection.lasteditor
{
key Currency,
Decimals,
CurrencyISOCode,
AlternativeCurrencyKey,
_Text[ Language = $session.system_language ].CurrencyName,
_Text[ Language = $session.system_language ].CurrencyShortName,
_Data.CurrencyComment,
_Data.Documentation,
_Data.PictureURL,
_Data.LastEditor,
_Data.ExcelAttachement,
_Data.ExcelMimetype,
_Data.ExcelFilename,
_Data.PictureAttachement,
_Data.PictureMimetype,
_Data.PictureFilename,
_Data.LastChanged,
_Data.LocalLastChanged,
_Country,
_User
}
Consumption
To complete the extension of the data model, we add the fields in the Consumption View.
define root view entity ZBS_C_DRPCurrency
provider contract transactional_query
as projection on ZBS_R_DRPCurrency
{
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 1.0
@Search.ranking: #HIGH
key Currency,
Decimals,
CurrencyISOCode,
AlternativeCurrencyKey,
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.7
@Search.ranking: #MEDIUM
CurrencyName,
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
@Search.ranking: #MEDIUM
CurrencyShortName,
CurrencyComment,
Documentation,
PictureURL,
ExcelAttachement,
ExcelMimetype,
ExcelFilename,
PictureAttachement,
PictureMimetype,
PictureFilename,
LastEditor,
_User.PersonFullName as EditorName,
_Country : redirected to composition child ZBS_C_DRPCurrencyCountry
}
Metadata Extension
In the Metadata Extension we also adopt the fields so that they are displayed in the UI. To do this we also create a new section in the "UI.Facet" on the object page.
{
id : 'idFiles',
label : 'Files',
position : 35,
type : #IDENTIFICATION_REFERENCE,
targetQualifier: 'FILE'
}
In the lower part we add the fields. You can hide the Mimetype and the file name and only show the Attachment field.
@UI:{
identification: [{ position: 80, qualifier: 'FILE' }]
}
@EndUserText.label: 'Excel'
ExcelAttachement;
@UI.hidden: true
ExcelMimetype;
@UI.hidden: true
ExcelFilename;
@UI:{
identification: [{ position: 80, qualifier: 'FILE' }]
}
@EndUserText.label: 'Picture'
PictureAttachement;
@UI.hidden: true
PictureMimetype;
@UI.hidden: true
PictureFilename;
Behavior
In the behavior definition we add the mapping so that the data ends up in the database when it is saved.
mapping for zbs_drp_addcurr
{
Currency = currency;
CurrencyComment = ccomment;
Documentation = documentation;
PictureURL = picture_url;
LastEditor = last_editor;
ExcelAttachement = excel_attachment;
ExcelMimetype = excel_mimetype;
ExcelFilename = excel_filename;
PictureAttachement = picture_attachment;
PictureMimetype = picture_mimetype;
PictureFilename = picture_filename;
LocalLastChanged = local_last_changed;
LastChanged = last_changed;
}
Result
If we now look at the current result in the UI, the fields are now there, but still look very much like standard input. At least the fields are already there and our extension is complete.
Upload Dialog
In this section we now want to add the upload dialog to the fields. To do this we only need to add a few annotations to prepare the fields.
Mimetype
We first need to set the Mimetype field with the new annotation from the "Semantics" area. This tells Fiori that there is a Mimetype in the field.
@Semantics.mimeType: true
ExcelMimetype,
Attachment
In the next step, we expand the attachment field with the necessary information and pass the fields for the mimetype and the filename. The information is needed for display and upload later.
@Semantics.largeObject: {
mimeType : 'ExcelMimetype',
fileName : 'ExcelFilename',
contentDispositionPreference: #INLINE
}
ExcelAttachement,
Result
The finished annotation looks like this. Since this is more of a technical annotation, we have transferred it to the Consumption View and not to the Metadata Extension.
@Semantics.largeObject: {
mimeType : 'ExcelMimetype',
fileName : 'ExcelFilename',
contentDispositionPreference: #INLINE
}
ExcelAttachement,
@Semantics.mimeType: true
ExcelMimetype,
ExcelFilename,
@Semantics.largeObject: {
mimeType : 'PictureMimetype',
fileName : 'PictureFilename',
contentDispositionPreference: #INLINE
}
PictureAttachement,
@Semantics.mimeType: true
PictureMimetype,
PictureFilename,
If we now look at the UI, the input fields in upload dialogs have changed. We can upload a file using the arrow and remove it again using the cross. The file name is displayed after the upload and is clickable. If you click on the name, the file is downloaded.
More tips
What else can we do with the content? In this section we want to go into two further points and describe them in more detail.
Restrictions
Would you like to restrict which files are uploaded? Then you can make further restrictions to the dialog using the annotation "Semantics.largeObject.acceptableMimeTypes". If you want to know which Mimetypes there are, you can find a list here.
@Semantics.largeObject: {
acceptableMimeTypes: [ 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ]
}
ExcelAttachement,
@Semantics.largeObject: {
acceptableMimeTypes: [ 'image/*' ]
}
PictureAttachement,
For Excel, we restrict to the two formats xls and xlsx, for this we make a list. For images, we restrict to all formats with "image", for this we can use an asterisk as a wildcard. If we then try to upload a file that does not match the filter, we receive the following error message in Fiori.
Usage
We can then also use the image that we load into our application, for example. For example, we can specify the field for the header.
@UI: {
headerInfo: {
imageUrl: 'PictureAttachement'
}
}
After we have loaded the image, it will also be displayed in the header. This means that in addition to the URL, the image can also be specified as an upload stream in the field.
Complete example
You can find all the changes we have made in the following commit of our GitHub repository on the topic of RAP development.
Conclusion
The simple upload of files into our application works easily and reliably. However, we then also save the file in our application, which requires storage space on the hard drive. The integrated upload as a popup will still take some time.