@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
412 lines (375 loc) • 12.6 kB
JavaScript
/*!
* 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;
});