UNPKG

@openui5/sap.m

Version:

OpenUI5 UI Library sap.m

412 lines (375 loc) 12.6 kB
/*! * OpenUI5 * (c) Copyright 2026 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ sap.ui.define([ "sap/base/Log", "sap/m/library", "sap/ui/core/Element", "sap/ui/core/util/File", "sap/ui/Device", "sap/m/upload/UploaderHttpRequestMethod", "sap/m/upload/UploadItem" ], function (Log, MobileLibrary, Element, FileUtil, Device, UploaderHttpRequestMethod, UploadItem) { "use strict"; /** * Constructor for a new Uploader. * * @class * A basic implementation for uploading and downloading one or multiple files. * * @extends sap.ui.core.Element * * @constructor * @public * @since 1.120 * @version 1.146.0 * @alias sap.m.upload.UploaderTableItem */ var Uploader = Element.extend("sap.m.upload.UploaderTableItem", { metadata: { library: "sap.m", publicMethods: [ "uploadItem", "downloadItem" ], properties: { /** * URL where the next file is going to be uploaded to. */ uploadUrl: {type: "string", defaultValue: null}, /** * URL where the next file is going to be downloaded from. */ downloadUrl: {type: "string", defaultValue: null}, /** * HTTP request method chosen for file upload. */ httpRequestMethod: {type: "sap.m.upload.UploaderHttpRequestMethod", defaultValue: UploaderHttpRequestMethod.Post}, /** * This property decides the type of request. If set to "true", the request gets sent as a multipart/form-data request instead of file only request. */ useMultipart: { type: "boolean", defaultValue: false } }, events: { /** * The event is fired just after the POST request is sent. */ uploadStarted: { parameters: { /** * The item {@link sap.m.upload.UploadItem UploadItem} that is going to be uploaded. */ item: {type: "any"} } }, /** * The event is fired every time an XHR request reports progress while uploading. */ uploadProgressed: { parameters: { /** * The item {@link sap.m.upload.UploadItem UploadItem} that is being uploaded. */ item: {type: "any"}, /** * The number of bytes transferred since the beginning of the operation. * This doesn't include headers and other overhead, but only the content itself */ loaded: {type: "int"}, /** * The total number of bytes of content that is transferred during the operation. * If the total size is unknown, this value is zero. */ total: {type: "int"} } }, /** * The event is fired when an XHR request reports successful completion of upload process. */ uploadCompleted: { parameters: { /** * The item {@link sap.m.upload.UploadItem UploadItem} that was uploaded. */ item: {type: "any"}, /** * A JSON object containing the additional response parameters like response, responseXML, readyState, status and headers. * <i>Sample response object:</i> * <pre><code> * { * response: "<!DOCTYPE html>\n<html>...</html>\n", * responseXML: null, * readyState: 2, * status: 404, * headers: "allow: GET, HEAD" * } * </code></pre> */ responseXHR: {type: "object"} } }, /** * The event is fired when an XHR request reports its termination. */ uploadTerminated: { parameters: { /** * The item that is going to be deleted. */ item: {type: "sap.m.upload.UploadItem"} } } } } }); Uploader.prototype.init = function () { this._mRequestHandlers = {}; }; /** * Starts the function for uploading one file object to a given URL. Returns promise that is resolved when the upload is finished, or is rejected when the upload fails. * * @param {File|Blob} oFile File or blob object to be uploaded. * @param {string} sUrl Upload Url. * @param {sap.ui.core.Item[]} [aHeaderFields] Collection of request header fields to be send along. * @returns {Promise} Promise that is resolved when the upload is finished, or is rejected when the upload fails. * @public */ Uploader.uploadFile = function (oFile, sUrl, aHeaderFields) { var oXhr = new window.XMLHttpRequest(); var sHttpRequestMethod = this.getHttpRequestMethod()?.toUpperCase(); return new Promise(function(resolve, reject) { oXhr.open(sHttpRequestMethod, sUrl, true); if ((Device.browser.edge || Device.browser.internet_explorer) && oFile.type && oXhr.readyState === 1) { oXhr.setRequestHeader("Content-Type", oFile.type); } if (aHeaderFields) { aHeaderFields.forEach(function (oHeader) { oXhr.setRequestHeader(oHeader.getKey(), oHeader.getText()); }); } oXhr.onreadystatechange = function () { if (this.readyState === window.XMLHttpRequest.DONE) { if (this.status === 200) { resolve(this); } else { reject(this); } } }; oXhr.send(oFile); }); }; /** * Starts the process of uploading the specified file. * * @param {sap.m.upload.UploadItem} oItem Item representing the file to be uploaded. * @param {sap.ui.core.Item[]} [aHeaderFields] Collection of request header fields to be send along. * @public */ Uploader.prototype.uploadItem = function (oItem, aHeaderFields) { var oXhr = new window.XMLHttpRequest(), oFile = oItem.getFileObject(), that = this, oRequestHandler = { xhr: oXhr, item: oItem }, sHttpRequestMethod = this.getHttpRequestMethod()?.toUpperCase(), sUploadUrl = oItem.getUploadUrl() || this.getUploadUrl(); const _isBrowserOffline = () => { return !window.navigator.onLine; }; const _fireBrowserOfflineEvent = () => { that.fireUploadCompleted({ item: oItem, responseXHR: { response: null, // No server response responseXML: null, responseText: JSON.stringify({ error: "Internet is offline. Please check your connection and try again." }), readyState: 4, // Indicates request has been processed (mocking completed state) status: 0, // 0 typically indicates a network error headers: "" } }); }; oXhr.open(sHttpRequestMethod, sUploadUrl, true); if ((Device.browser.edge || Device.browser.internet_explorer) && oFile.type && oXhr.readyState === 1) { oXhr.setRequestHeader("Content-Type", oFile.type); } if (aHeaderFields) { aHeaderFields.forEach(function (oHeader) { oXhr.setRequestHeader(oHeader.getKey(), oHeader.getText()); }); } oXhr.upload.addEventListener("progress", function (oEvent) { that.fireUploadProgressed({ item: oItem, loaded: oEvent.loaded, total: oEvent.total, aborted: false }); }); oXhr.onreadystatechange = function () { var oHandler = that._mRequestHandlers[oItem.getId()], oResponseXHRParams = {}; if (this.readyState === window.XMLHttpRequest.DONE && !oHandler.aborted) { oResponseXHRParams = { "response": this.response, "responseXML": this.responseXML, "responseText": this.responseText, "readyState": this.readyState, "status": this.status, "headers": this.getAllResponseHeaders() }; that.fireUploadCompleted({item: oItem, responseXHR: oResponseXHRParams}); } }; if (this.getUseMultipart()) { var oFormData = new window.FormData(); var name = oFile ? oFile.name : null; if (oFile instanceof window.Blob && name) { oFormData.append(name, oFile, oFile.name); } else { oFormData.append(name, oFile); } oFormData.append("_charset_", "UTF-8"); oFile = oFormData; this._mRequestHandlers[oItem.getId()] = oRequestHandler; if (_isBrowserOffline()) { this.fireUploadStarted({item: oItem}); _fireBrowserOfflineEvent(); } else { oXhr.send(oFile); this.fireUploadStarted({item: oItem}); } } else { this._mRequestHandlers[oItem.getId()] = oRequestHandler; if (_isBrowserOffline()) { this.fireUploadStarted({item: oItem}); _fireBrowserOfflineEvent(); } else { oXhr.send(oFile); this.fireUploadStarted({item: oItem}); } } }; /** * Starts the process of downloading a file. Plugin uses the URL set in the item or the downloadUrl set in the uploader class to download the file. * If the URL is not set, a warning is logged. * API downloads the file with xhr response of blob type or string type. * * @param {sap.m.upload.UploadItem} oItem Item representing the file to be downloaded. * @param {sap.ui.core.Item[]} aHeaderFields List of header fields to be added to the GET request. * @param {boolean} bAskForLocation If it is true, the location of where the file is to be downloaded is queried by a browser dialog. * @return {boolean} It returns true if the download is processed successfully * @public */ Uploader.prototype.download = function (oItem, aHeaderFields, bAskForLocation) { var sUrl = this.getDownloadUrl() || oItem.getUrl(); // File.save doesn't work in Safari, however, URLHelper.redirect works. // So, this overwrites the value of bAskForLocation in order to make it work. if (Device.browser.name === "sf") { bAskForLocation = false; } if (!oItem.getUrl()) { Log.warning("Items to download do not have a URL."); return false; } else if (bAskForLocation) { var oResponse = null, oXhr = new window.XMLHttpRequest(); oXhr.open("GET", sUrl); aHeaderFields.forEach(function (oHeader) { oXhr.setRequestHeader(oHeader.getKey(), oHeader.getText()); }); oXhr.responseType = "blob"; // force the HTTP response, response-type header to be blob oXhr.onload = function () { let targetItem = UploadItem; if (oItem instanceof UploadItem) { targetItem = UploadItem; } var sFileName = oItem.getFileName(), oSplit = targetItem._splitFileName(sFileName, false); oResponse = oXhr.response; if (oResponse instanceof window.Blob) { const sFullFileName = `${oSplit.name}.${oSplit.extension}`; this._saveAsFile(oResponse, sFullFileName); } else if (typeof oResponse === "string") { FileUtil.save(oResponse, oSplit.name, oSplit.extension, oItem.getMediaType(), "utf-8"); } }.bind(this); oXhr.send(); return true; } else { MobileLibrary.URLHelper.redirect(sUrl, true); return true; } }; /** * Attempts to terminate the process of uploading the specified file. * * @param {sap.m.upload.UploadItem} oItem Item representing the file whose ongoing upload process is to be terminated. * @public */ Uploader.prototype.terminateItem = function (oItem) { var oHandler = this._mRequestHandlers[oItem.getId()], that = this; oHandler.xhr.onabort = function () { oHandler.aborted = false; that.fireUploadTerminated({item: oItem}); }; oHandler.aborted = true; oHandler.xhr.abort(); }; /** * This function saves the provided Blob to the local file system. * The parameter name is optional and depending on the browser it * is not ensured that the filename can be applied. Google Chrome, * Mozilla Firefox, Internet Explorer and Microsoft Edge will * apply the filename correctly. * * @param {Blob} oBlob - Binary large object of the file that should be saved to the filesystem * @param {string} [sFilename] - Filename of the file including the file extension * @private */ Uploader.prototype._saveAsFile = function(oBlob, sFilename) { let fnSave; /* Ignore other formats than Blob */ if (!(oBlob instanceof Blob)) { return; } const oLink = document.createElement("a"); const oDownloadSupported = "download" in oLink; if (oDownloadSupported) { fnSave = function(data, fileName) { oLink.download = fileName; oLink.href = window.URL.createObjectURL(data); oLink.dispatchEvent(new window.MouseEvent("click")); }; } function openWindow(sUrl, sWindowName) { const sWindowFeatures = "noopener,noreferrer"; return window.open(sUrl, sWindowName, sWindowFeatures); } /* In case of pre iOS 13 Safari, MacOS Safari (legacy) */ if (typeof fnSave === "undefined") { fnSave = function(data) { const oReader = new window.FileReader(); oReader.onloadend = function() { const sUrl = oReader.result.replace(/^data:[^;]*;/, "data:attachment/file;"); const opened = openWindow(sUrl, "_blank"); if (!opened) { window.location.href = sUrl; } }; oReader.readAsDataURL(data); }; } /* Save file to device */ fnSave(oBlob, sFilename); }; return Uploader; });