Introduction
This blog helps UI5/Fiori developers understand sap.m.uploader.UploadSet UI Element in detail.
Problem
The SAP UI5 Demo site explains the UploadSet sample application. However, the site can not explain how it works with a back-end system.
Solution
Below is the step by step working example of the UploadSet UI element that I have implement. Hope this helps clarify many questions related to UploadSet.
Back-end Gateway Service Implementation
Build a Gateway Project, create an entity for attachment attributes shown below.
Mark the entity as Media
Re-define Define method of the MPC Extension class to mark the Mimetype field as content type.
method DEFINE.
super->define( ).
data: lo_entity TYPE REF TO /iwbep/if_mgw_odata_entity_typ,
lo_property TYPE REF TO /iwbep/if_mgw_odata_property.
lo_entity = model->get_entity_type( iv_entity_name = 'Attachment' ).
IF lo_entity IS BOUND.
lo_property = lo_entity->get_property( iv_property_name = 'Mimetype').
lo_property->set_as_content_type( ).
lo_property = lo_entity->get_property( iv_property_name = 'Erdat' ).
lo_property->/iwbep/if_mgw_odata_annotatabl~create_annotation( 'sap' )->add(
iv_key = 'display-format' iv_value = 'Date' ).
ENDIF.
endmethod.
Its time to implement the Data Provider class. Starting with re-defining method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~CREATE_STREAM.
this method is called when an file is being attached. The most important information we need here is the content of the file, MIME type, filename, Key ( against which we are going to save this attachment ). We are making use of importing parameter iv_slug to get some of the information. We will know how to pass this information from UI when we code for UI5.
method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~CREATE_STREAM.
DATA ls_attach TYPE ZUI_ATTACHMENTS_S.
DATA lv_xstring TYPE xstring.
DATA lr_attachments TYPE REF TO zcl_ut_ui5_attachments.
* READ TABLE it_key_tab ASSIGNING FIELD-SYMBOL(<fs_key>) INDEX 1.
SPLIT iv_slug AT ',' INTO ls_attach-filename
ls_attach-ref_guid
ls_attach-obj_name.
ls_attach-mimetype = is_media_resource-mime_type.
ls_attach-erdat = sy-datum.
ls_attach-ernam = sy-uname.
lf_data = is_media_resource-value.
** All the attachment information needed to save the file is obtained
** Now its time to save it to database/file system. Below code does that.
CREATE OBJECT lr_attachments.
ls_attach-guid = lr_attachments->create_attachment(
exporting i_attach = ls_attach
iv_content = lf_data
).
** Return the etntity back to the caller
copy_data_to_ref( EXPORTING is_data = ls_attach
CHANGING cr_data = er_entity ).
Implement method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_STREAM to download the file
method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_STREAM.
data ls_stream type ty_s_media_resource.
DATA lr_attachments TYPE REF TO zcl_ut_ui5_attachments.
DATA ls_attach TYPE zui_attachments.
DATA ls_header type ihttpnvp.
DATA lv_guid TYPE guid_32.
** Read the Key Information to obtain the file details
READ TABLE it_key_tab INTO DATA(ls_key_tab) INDEX 1.
IF sy-subrc <> 0.
RETURN.
ENDIF.
lv_guid = ls_key_tab-value.
** Once the Key for the attachment is fetched. Its time to read the content
** from database/file system wherever it is stored
CREATE OBJECT lr_attachments.
lr_attachments->read_attachment( EXPORTING i_guid = lv_guid
IMPORTING es_attach_data = ls_attach
ev_content = ls_stream-value ).
** Send back the attachment content to the caller.
ls_header-name = 'Content-Disposition'.
CONCATENATE 'attachment; filename="' ls_attach-filename '"' INTO ls_header-value.
set_header( is_header = ls_header ).
ls_stream-mime_type = ls_attach-mimetype.
copy_data_to_ref( exporting is_data = ls_stream changing cr_data = er_stream ).
endmethod.
Implement GET_ENTITYSET method to read all the attachment information associated to a particular document. ( RefGuid is the field that identifies all the attachments associated with it )
method ATTACHMENTSET_GET_ENTITYSET.
DATA lv_ref_guid type guid_32.
DATA lr_attachments TYPE REF TO zcl_ut_ui5_attachments.
** Get the RefGuid value to read its associated attachments
read table it_filter_select_options INTO data(ls_filter) INDEX 1.
IF sy-subrc = 0.
READ TABLE ls_filter-select_options INTO data(ls_options)
INDEX 1.
IF sy-subrc = 0.
lv_ref_guid = ls_options-low.
ENDIF.
ENDIF.
CHECK lv_ref_guid IS NOT INITIAL.
*** Your own logic to read the attachments information.
CREATE OBJECT lr_attachments.
TRY.
lr_attachments->get_all_attachment( exporting i_ref_guid = lv_ref_guid
importing rt_attachment = DATA(lt_attach) ).
SORT lt_attach BY erdat.
** Fill up the return entity
LOOP AT lt_attach INTO DATA(ls_attach).
APPEND INITIAL LINE TO et_entityset ASSIGNING FIELD-SYMBOL(<ls_entityset>).
<ls_entityset>-filename = ls_attach-filename.
<ls_entityset>-erdat = ls_attach-erdat.
<ls_entityset>-created_by = ls_attach-created_by.
<ls_entityset>-guid = ls_attach-guid.
ENDLOOP.
CATCH ZCX_ATTACHMENT.
ENDTRY.
endmethod.
Front-End Implementation
This UI5 code works from UI5 version 1.90.0. We are going to implement Attachments using sap.m.upload.UploadSet UI element.
Here is the XML code for the attachments view.
<mvc:View controllerName="test.ztest.controller.View1" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m"
xmlns:core="sap.ui.core" xmlns:upload="sap.m.upload">
<Shell id="shell">
<App id="app">
<pages>
<Page id="idPage">
<upload:UploadSet id="idAttach" showIcons="true" uploadEnabled="true" terminationEnabled="true"
uploadUrl="/sap/opu/odata/sap/ZATTACHMENTS_SRV/AttachmentSet" instantUpload="true" items="{path: 'oModelAttach>/items' }"
beforeUploadStarts="onBeforeUploadStarts" uploadCompleted="onUploadComplete">
<upload:items>
<upload:UploadSetItem fileName="{oModelAttach>fileName}" visibleRemove="true" removePressed="onRemovePressed">
<upload:attributes>
<ObjectAttribute title="File Description" text="{oModelAttach>fileName}"/>
</upload:attributes>
</upload:UploadSetItem>
</upload:items>
</upload:UploadSet>
</Page>
</pages>
</App>
</Shell>
</mvc:View>
Controller code to Read the existing attachments for a document ( which is RefGuid in our case )
this.getOwnerComponent().getModel().read("/AttachmentSet", {
filters: [new Filter("RefGuid", FilterOperator.EQ, "1234")],
success: function (oData) {
var json = new JSONModel([]);
json.items = [];
for (var i = 0; i < oData.results.length; i++) {
var item = {
Guid: oData.results[i].Guid,
fileName: oData.results[i].Filename,
meddiaType: oData.results[i].Mimetype,
url: "/sap/opu/odata/sap/ZATTACHMENTS_SRV/AttachmentSet('" + oData.results[i].Guid + "')/$value",
uploadState: "Complete",
CreatedBy: oData.results[i].CreatedBy,
Erdat: oData.results[i].Erdat,
selected: false
};
json.items.push(item);
}
this.getView().getModel("oModelAttach").setData(json);
}.bind(this),
error: function (oError) {
sap.m.MessageToast.show("Error occured reading data");
}
});
instantUpload property of UploadSet starts sending the file as soon we choose file or drag and drop on to UI element. We need to send the RefGuid ( Document number, other information if needed ) along with the request. Here comes the use of iv_slug that we have learnt while implementing DPC_EXT. I am using a comma separated list of fields in slug.
We are making use of beforeUploadStarts event of UploadSet to pass that information.
onBeforeUploadStarts: function (oEvent) {
var oHeaderItem = oEvent.getParameter("item"),
slugVal = oHeaderItem.getFileName() + ",1234,ZSD_SAMPLES";
oHeaderItem.removeAllStatuses();
oHeaderItem.addHeaderField(new sap.ui.core.Item({
key: "slug",
text: slugVal
}));
oHeaderItem.addHeaderField(new sap.ui.core.Item({
key: "x-csrf-token",
text: this.getOwnerComponent().getModel().getSecurityToken()
}));
}
Finally, Implementing onUploadComplete event to complete the upload and setAttachmentModel to read the attachments for a particular document (RefGuid in our case)
onUploadComplete: function (oEvent) {
var oStatus = oEvent.getParameter("status"),
oItem = oEvent.getParameter("item"),
oUploadSet = this.getView().byId("idAttach");
if (oStatus && oStatus !== 201) {
oItem.setUploadState("Error");
oItem.removeAllStatuses();
} else {
oUploadSet.removeIncompleteItem(oItem);
this.setAttachmentModel();
}
},
setAttachmentModel: function () {
this.getOwnerComponent().getModel().read("/AttachmentSet", {
filters: [new Filter("RefGuid", FilterOperator.EQ, "1234")],
success: function (oData) {
var json = new JSONModel([]);
json.items = [];
for (var i = 0; i < oData.results.length; i++) {
var item = {
Guid: oData.results[i].Guid,
fileName: oData.results[i].Filename,
meddiaType: oData.results[i].Mimetype,
url: "/sap/opu/odata/sap/ZATTACHMENTS_SRV/AttachmentSet('" + oData.results[i].Guid + "')/$value",
uploadState: "Complete",
CreatedBy: oData.results[i].CreatedBy,
Erdat: oData.results[i].Erdat,
selected: false
};
json.items.push(item);
}
this.getView().getModel("oModelAttach").setData(json);
}.bind(this),
error: function (oError) {
sap.m.MessageToast.show("Error occured reading data");
}
});
}
Let’s see how it works
You can click on upload/ drag and drop files on to the UI element to upload. You can see the status of the uploading file.
Conclusion
The key point here while building this application is to know how to pass the additional parameters to the back-end with the help of slug header parameter from the front-end and how to handle it in the back-end.
References
API Reference: https://sapui5.hana.ondemand.com/#/api/sap.m.upload.UploadSet
Sample in Demo site: https://sapui5.hana.ondemand.com/#/entity/sap.m.upload.UploadSet
Hope this blog is helpful for you. Feel free to comment/suggest, like, and share.