Multiupload multiple files one by one in SAP UI5 (Sync Multiple Files Upload)

Introduction

Boost your productivity with the power of synchronous multiple file upload in SAP UI5. With our innovative solution, you can easily and efficiently upload multiple files one by one, simplifying your workflow and saving valuable time. Say goodbye to the hassle of uploading files individually and embrace the convenience of synchronous file uploads. Streamline your processes, enhance collaboration, and improve efficiency with Sync Multiple Files Upload in SAP UI5. Experience a seamless and optimized file uploading experience like never before.

Difference between Sync and Async File Uploads in JavaScript

Synchronous (Sync) and asynchronous (Async) file uploads in JavaScript refer to two different approaches for handling file upload operations. Here’s a breakdown of the key differences between the two:

1. Execution Flow:
– Sync: In synchronous file uploads, the execution of code pauses until the file upload operation is complete. It means that other code or operations won’t execute until the upload is finished.
– Async: Asynchronous file uploads, on the other hand, allow the execution of code to continue while the file upload operation is in progress. The code doesn’t block and waits for the upload to complete.

2. User Experience:
– Sync: Synchronous file uploads can potentially cause the user interface to freeze or become unresponsive during the upload process. This is because the entire upload operation blocks the execution of other code or user interactions until it finishes.
– Async: Asynchronous file uploads provide a better user experience as they allow the user interface to remain responsive while the upload is happening. Users can continue interacting with the application without any interruptions.

3. Code Structure:
– Sync: Synchronous file upload code is typically written in a linear and sequential manner. The code execution waits for the upload to complete before proceeding to the next line of code.
– Async: Asynchronous file upload code is structured using callbacks, promises, or async/await syntax. It allows for non-blocking execution, enabling other code or operations to run concurrently while the upload progresses.

4. Scalability and Performance:
– Sync: Synchronous file uploads may not be suitable for large file uploads or scenarios with high concurrency. They can impact the scalability and performance of the application, especially if multiple users are uploading files simultaneously.
– Async: Asynchronous file uploads are more scalable and performant as they allow for concurrent execution of multiple operations. This is particularly beneficial when dealing with large file uploads or multiple users uploading files concurrently.

5. Error Handling:
– Sync: Synchronous file uploads often handle errors using exception handling mechanisms. If an error occurs during the upload process, it can lead to program crashes or disruptions in the code flow.
– Async: Asynchronous file uploads provide better error handling capabilities. They allow for the use of error callbacks, rejection handling in promises, or try-catch blocks with async/await, enabling more graceful error handling and recovery options.

In summary, synchronous file uploads block the execution of other code until the upload is complete, while asynchronous file uploads allow concurrent execution, providing a better user experience, scalability, and performance. Asynchronous approaches are typically preferred for modern web applications where responsiveness and efficiency are crucial.

When to choose Sync File Upload?

Synchronous file uploads can be appropriate in certain scenarios where the following conditions are met:

1. Simplicity: If the file upload operation is simple and doesn’t involve complex processing or interactions with other parts of the application, synchronous file uploads can be a straightforward solution. They have a linear code structure, making it easier to understand and implement.

2. Small File Sizes: Synchronous file uploads can be suitable for small file sizes that can be uploaded quickly without causing significant delays or freezing the user interface. If the file sizes are small and the upload process is expected to complete rapidly, synchronous uploads may be sufficient.

3. Sequential Dependencies: If the file upload operation depends on the completion of other synchronous tasks or operations in your application, a synchronous approach can simplify the coordination between these steps. For example, if the file upload is tightly linked with specific data processing or validation steps that must occur sequentially, synchronous file uploads can ensure the proper order of execution.

4. Simultaneous Blocking: In some cases, blocking the user interface during the file upload might be desired. For instance, in certain security-conscious applications where it is crucial to ensure that no other actions can be performed while the file is being uploaded, synchronous file uploads can be employed.

It’s important to note that synchronous file uploads may not be suitable for scenarios involving large file sizes, complex operations, or situations where multiple users are simultaneously uploading files. In such cases, asynchronous file uploads are generally preferred to maintain a responsive user interface and better scalability.

Multiupload multiple files one by one in SAP UI5 (Sync Multiple Files Upload)

View Code

<mvc:View controllerName="TestProject.TestProject.controller.Main" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m">
<Shell id="shell">
<App id="app">
<pages>
<Page id="page" title="My Project Ideas: Multiupload multiple files one by one in UI5 (Sync Multiple Upload files)">
<content>
<UploadCollection id="idMultiUploader" maximumFilenameLength="55" maximumFileSize="3" multiple="true" sameFilenameAllowed="false"
instantUpload="false" noDataDescription="{i18n>noDataDescriptiont}" change="onChangeEmailUpload" fileDeleted="onFileDeleted"
filenameLengthExceed="onFilenameLengthExceed" fileSizeExceed="onFileSizeExceed" typeMissmatch="onTypeMissmatchMultiUpload"
uploadComplete="onUploadComplete" beforeUploadStarts="onBeforeUploadStarts"/>
<Button text="{i18n>Upload}" press="onMultiUploadSubmit"/>
<Button id="messagePopoverBtn" icon="sap-icon://message-popup" type="{popoverModel>/type}" text="{popoverModel>/messagesLength}"
press="handleMessagePopoverPress"/>
</content>
</Page>
</pages>
</App>
</Shell>
</mvc:View>

 

Controller Code

sap.ui.define([
    "sap/ui/core/mvc/Controller",
    "sap/ui/core/BusyIndicator",
    "sap/m/MessageBox",
    "sap/m/MessagePopover",
    "sap/m/MessagePopoverItem",
    "sap/m/Button"
], function (Controller, BusyIndicator, MessageBox, MessagePopover, MessagePopoverItem, Button) {
    "use strict";
    var oMessageTemplate;
    var oMessagePopover;
    return Controller.extend("TestProject.TestProject.controller.Main", {
        onInit: function () {
            var headerButton = new sap.m.Button({
                text: "Clear",
                type: sap.m.ButtonType.Reject,
                press: function () {
                    that.onClearNotification();
                }
            });
            oMessageTemplate = new MessagePopoverItem({
                type: '{T}',
                title: '{S}',
            });
            oMessagePopover = new MessagePopover({
                items: {
                    path: '/',
                    template: oMessageTemplate
                },
                headerButton: headerButton
            });
            var pop_msgModel = new sap.ui.model.json.JSONModel({
                "messagesLength": "",
                "type": "Default"
            });
            this.getView().setModel(pop_msgModel, "popoverModel");
            var popModel = new sap.ui.model.json.JSONModel({});
            oMessagePopover.setModel(popModel);
            // Uploader Files
            this.contentFiles = [];
            this.contentSize = 0;
        },

        /**
         * onUploadContentTable is invoked on click from UI. 
         * Input: Excel Data
         * Output: Excel Preview
         * Excel upload is implemented in BaseController.js
         */

        onUploadContentTable: function (oEvent) {
            BusyIndicator.show();
            this.file = oEvent.getParameter("files")[0];
            this.UploadUrl = "/DataManager/masterData/contentTable";
            this.getExcelPreview(this.file, oEvent);
            this.byId("uploadContentTableTemplate").setValue("");
            BusyIndicator.hide();
        },

        /**
         * onMultiUploadCancel is invoked on click from UI. 
         * Input: oEvent
         * Output: Deletion of all Items of Dialog Box and Dialog Box closes
         */

        onMultiUploadCancel: function (oEvent) {},

        /**
         * onChangeEmailUpload is invoked on drag and drop of files. 
         * Input: Single or Multiple Excel Data
         * Output: All files are added within an array and used during submit
         */

        onChangeEmailUpload: function (oEvent) {
            var that = this;
            if (oEvent.getParameters("files").files[0]) {
                // if files added via add button
                var allData = Object.values(oEvent.getParameters("files").files);
                allData.forEach(function (data) {
                    that.contentFiles = that.contentFiles.concat(data);
                });
            } else {
                // if files added via drag and drop
                this.contentFiles = this.contentFiles.concat(oEvent.getParameter("files"));
            }
        },

        /**
         * onMultiUploadSubmit is invoked on click from UI. 
         * Input: Single or Multiple Excel Data
         * Output: Messages in Popover
         * Table rebind is implemented in BaseController.js
         * It is a recurring function that uploads file on every succcess of previous one
         */

        onMultiUploadSubmit: function (oEvent, index) {
            var that = this;
            var formData;
            var sPercentage;
            this.UploadUrl = "/DataManager/masterData/contentTable";
            BusyIndicator.show(0);
            if (!index) {
                index = 0;
                // Progress Bar to show upload in percentage
                if (!that._oProgressDialog) {
                    that._oProgressDialog = sap.ui.xmlfragment("TestProject.TestProject.fragment.progressIndicator", that);
                    that.getView().addDependent(that._oProgressDialog);
                }
                that._oProgressDialog.open();
            }
            if (that.contentFiles.length > 0 && index < that.contentFiles.length) {
                formData = that.contentFiles[index];
                var form = new FormData();
                form.append("file", formData);
                return $.ajax({
                    method: "PUT",
                    url: "/DataManager/masterData/contentTable",
                    data: form,
                    async: true,
                    processData: false,
                    mimeType: "multipart/form-data",
                    headers: {
                        "X-Csrf-Token": "<Get token from Data Model>"
                    },
                    contentType: false,
                }).done((response) => {
                    var successData = [];
                    successData.push({
                        T: "Success",
                        S: that.contentFiles[index].name + " : " + response
                    });
                    // Updating the Message Popover
                    var previous = oMessagePopover.getModel().getData();
                    if (previous.length === undefined)
                        previous = [];
                    var updated = previous != "" ? previous.concat(successData) : successData;
                    oMessagePopover.getModel().setData(updated);
                    oMessagePopover.getModel().refresh(true);
                    that.getView().getModel("popoverModel").getData().messagesLength = updated.length;
                    that.getView().getModel("popoverModel").getData().type = "Emphasized";
                    that.getView().getModel("popoverModel").refresh(true);
                    // Updating the progress Indicator
                    sPercentage = parseInt(((index + 1) / that.contentFiles.length) * 100, 0);
                    that.getOwnerComponent().getModel("appConfigModel").setProperty("/uploadPercentage", sPercentage);
                    that.onMultiUploadSubmit(oEvent, index + 1);
                }).fail(function (XMLHttpRequest, textStatus, errorThrown) {
                    // Updating the Message Popover
                    var sErrorMsg = that.getErrorMsg(XMLHttpRequest, textStatus, errorThrown);
                    if (sErrorMsg) {
                        var errorData = [];
                        errorData.push({
                            T: "Error",
                            S: that.contentFiles[index].name + " : " + sErrorMsg
                        });
                        var previous = oMessagePopover.getModel().getData();
                        if (previous.length === undefined)
                            previous = [];
                        var updated = previous != "" ? previous.concat(errorData) : errorData;
                        oMessagePopover.getModel().setData(updated);
                        oMessagePopover.getModel().refresh(true);
                        that.getView().getModel("popoverModel").getData().messagesLength = updated.length;
                        that.getView().getModel("popoverModel").getData().type = "Emphasized";
                        that.getView().getModel("popoverModel").refresh(true);
                        // Updating the progress Indicator
                        sPercentage = parseInt(((index + 1) / that.contentFiles.length) * 100, 0);
                        that.getOwnerComponent().getModel("appConfigModel").setProperty("/uploadPercentage", sPercentage);
                        that.onMultiUploadSubmit(oEvent, index + 1);
                    }
                });
            } else {
                that.contentFiles = [];
                BusyIndicator.hide();
                // Updating the progress Indicator
                sPercentage = parseInt(((index + 1) / that.contentFiles.length) * 100, 0);
                that.getOwnerComponent().getModel("appConfigModel").setProperty("/uploadPercentage", sPercentage);
                that._oProgressDialog.close();
            }
        },

        /**
         * handleMessagePopoverPress is invoked on click from UI. 
         * Input: oEvent
         * Output: Message Popover opens up or closes
         */

        handleMessagePopoverPress: function (oEvent) {
            oMessagePopover.toggle(oEvent.getSource());
        },

        /**
         * onFileSizeExceed is invoked on click of Upload in UI. 
         * Input: Size of file
         * Output: Error Message
         */

        onFileSizeExceed: function (oEvent) {
            // read msg from i18n model
            var sMsg = this.oBundle.getText("MultiUploadCondition");
            MessageBox.error(sMsg);
        },

        /**
         * onTypeMissmatchMultiUpload is invoked click of Upload in UI. 
         * Input: Type of file
         * Output: Error Message
         * The type miss match generic function is available in base controller
         */

        onTypeMissmatchMultiUpload: function (oEvent) {
            // this.onTypeMissmatch();
        },

        /**
         * onClearNotification is invoked on click of Clear from UI. 
         * Input: Message Model
         * Output: Empty Message Model
         */

        onClearNotification: function (oEvent) {
            // Clear Popover Messages
            this.getView().getModel("popoverModel").setData({
                "messagesLength": "",
                "type": "Default"
            });
            this.getView().getModel("popoverModel").refresh(true);
            oMessagePopover.getModel().setData("");
            oMessagePopover.getModel().refresh(true);
        },

        /**
         * onResetForms is invoked from a function. 
         * Input: array, object or string
         * Output: Input field value is cleared
         */

        getErrorMsg: function (XMLHttpRequest, textStatus, errorThrown) {
            var sCommonErrorMsg = "An error caught";
            var sTimeOutErrorMsg = "Time out";
            var sErrorMsg;
            if (XMLHttpRequest.responseJSON) {
                if (XMLHttpRequest.responseJSON.message) {
                    if (Array.isArray(XMLHttpRequest.responseJSON.message)) {
                        sErrorMsg = XMLHttpRequest.responseJSON.message.join("\n");
                    } else {
                        sErrorMsg = XMLHttpRequest.responseJSON.message;
                    }
                } else {
                    sErrorMsg = XMLHttpRequest.responseJSON.error;
                }
            } else if (XMLHttpRequest.responseText) {
                if (XMLHttpRequest.responseText == "Unauthorized") {
                    sErrorMsg = sTimeOutErrorMsg;
                    window.location.href = '../logout';
                } else {
                    sErrorMsg = XMLHttpRequest.responseText;
                }
            } else {
                sErrorMsg = sCommonErrorMsg;
            }
            return sErrorMsg;
        }
    });
});

 

progressIndicator.fragment.xml Code

<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:html="http://www.w3.org/1999/xhtml">
    <Dialog id="idProgresIndicatorDialog" title="{i18n>UploadinProgress}" titleAlignment="Center" showHeader="true"
        class="sapUiResponsivePadding">
        <ProgressIndicator id="idProgresIndicator" class="sapUiSmallMarginBottom" displayValue="{appConfigModel>/uploadPercentage}%"
            percentValue="{appConfigModel>/uploadPercentage}" state="Information" displayOnly="true"/>
    </Dialog>
</core:FragmentDefinition>

 

Model Code

sap.ui.define([
    "sap/ui/model/json/JSONModel",
    "sap/ui/Device"
], function (JSONModel, Device) {
    "use strict";

    return {

        createDeviceModel: function () {
            var oModel = new JSONModel(Device);
            oModel.setDefaultBindingMode("OneWay");
            return oModel;
        },
        
        createAppConfigData: function () {
            var oAppConfigModel = {
                "uploadPercentage": 0
            };
            return oAppConfigModel;
        }

    };
});

 

Component.js Code

sap.ui.define([
    "sap/ui/core/UIComponent",
    "sap/ui/Device",
    "TestProject/TestProject/model/models",
    "sap/ui/model/json/JSONModel",
], function (UIComponent, Device, models, JSONModel) {
    "use strict";

    return UIComponent.extend("TestProject.TestProject.Component", {

        metadata: {
            manifest: "json"
        },

        /**
         * The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
         * @public
         * @override
         */
        init: function () {
            // call the base component's init function
            UIComponent.prototype.init.apply(this, arguments);

            // enable routing
            this.getRouter().initialize();

            // set the device model
            this.setModel(models.createDeviceModel(), "device");
            
                        //set AppConfigModel
            this.setModel(new JSONModel(models.createAppConfigData()), "appConfigModel");
        }
    });
});

 

Output

Before Upload

Multiupload multiple files one by one in SAP UI5 Before Upload

Progress Indicator

Progress Indicator

After Upload

Sync Multiple Files Upload

 

Note: Update i18n wherever you see the wrong texts, they are intended to be shown like that in the absence of i18n files

Author

  • Barry Allen

    A Full Stack Developer with 10+ years of experience in different domain including SAP, Blockchain, AI and Web Development.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.