SAP HANA XS File upload with UI5 - sapui5

I'm trying to implement a file upload in a UI5 application on a HANA XS Server.
I can't find many informations how to do that - somebody got an idea?

here's the simple implementation of a plain text file upload:
Client side js:
doUpload: function() {
var uploadField = document.getElementById("ulUploader1-fu");
var file = uploadField.files[0];
var reader = new FileReader();
reader.onload = function (event) {
var source = event.target.result; // this is the binary values
var name = file.name;
$.ajax({
url: "/services/upload.xsjs?cmd=Import",
type: "PUT",
processData: false,
contentType: file.type,
data: source,
xhr: function() {
var req = $.ajaxSettings.xhr();
if (req) {
if (req.overrideMimeType) {
req.overrideMimeType('text/plain; charset=x-user-defined');
}
if (req.sendAsBinary) {
req.send = req.sendAsBinary;
}
}
return req;
},
error: function(xhr, textStatus, errorThrown){
alert(xhr.responseText);
},
success: function() {
reader.onload = null;
}
});
};
reader.readAsText(file);
}
And here's the serverside xsjs service:
function doImport() {
var data = '', conn = $.db.getConnection(), pstmt;
if($.request.body){
data = $.request.body.asString();
}
var conn = $.db.getConnection();
var pstmt = conn.prepareStatement( 'INSERT INTO "TEST"."UPLOAD" (ID, MIMETYPE, DATA) VALUES(?,?,?)' );
pstmt.setInteger(1,1);
pstmt.setString(2,"text/plain");
pstmt.setString(3,data);
pstmt.execute();
pstmt.close();
conn.commit();
conn.close();
doResponse(200,'');
$.response.contentType = 'text/plain';
$.response.setBody('Upload ok');
$.response.status = 200;
}

There is no "ready-to-consume" service from XS that allows you to do that. You can of course create a table in HANA DB, create a column-type BLOB and then build service on XS that allows you to upload file from your front-end. I hope that helps.

Related

upload file into document library in sharepoint with column value

Iam trying to upload file into document library but I can able to upload file but however column data was not reflecting into the column of document library.using rest Api every thing is working but my column value was not showing inside the column
here is my code
function createListItem() {
debugger;
var files = $("#attachment")[0].files;
if (files.length > 0) {
fileName = files[0].name;
var webUrl = _spPageContextInfo.webAbsoluteUrl;
var documentLibrary = "MyDocumets";
var targetUrl = _spPageContextInfo.webServerRelativeUrl + "/" + documentLibrary;
// Construct the Endpoint
var url = webUrl + "/_api/Web/GetFolderByServerRelativeUrl(#target)/Files/add(overwrite=true, url='" + fileName + "')?#target='" + targetUrl + "'&$expand=ListItemAllFields";
uploadFileToFolder(files[0], url, function(data) {
var file = data.d;
DocFileName = file.Name;
var updateObject = {
__metadata: {
type: file.ListItemAllFields.__metadata.type},
"DocumentType": $('#documenttype').val(),
"DocumentDescription": $("#Description").val(),
FileLeafRef: DocFileName //FileLeafRef --> Internal Name for Name Column
};
alert("File uploaded successfully!");
}, function(data) {
alert("File uploading failed");
});
} else {
alert("Kindly select a file to upload.!")
}
}
function uploadFileToFolder(fileObj, url, success, failure) {
var apiUrl = url;
// Initiate method calls using jQuery promises.
// Get the local file as an array buffer.
var getFile = getFileBuffer(fileObj);
// Add the file to the SharePoint folder.
getFile.done(function(arrayBuffer) {
$.ajax({
url: apiUrl,//File Collection Endpoint
type: "POST",
data: arrayBuffer,
processData: false,
async: false,
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": jQuery("#__REQUESTDIGEST").val(),
},
success: function(data) {
success(data);
},
error: function(data) {
success(data);
}
});
});
}
// Get the local file as an array buffer.
function getFileBuffer(uploadFile) {
var deferred = jQuery.Deferred();
var reader = new FileReader();
reader.onloadend = function(e) {
deferred.resolve(e.target.result);
}
reader.onerror = function(e) {
deferred.reject(e.target.error);
}
reader.readAsArrayBuffer(uploadFile);
return deferred.promise();
}
i wanted to know how to enter value into the column and upload the document at the sane time

Uploading a file using the File Uploader module

I am trying to upload a file using the FileUploader module in SAPUI5. The code I am trying to follow is from a blog https://blogs.sap.com/2016/11/08/step-by-step-on-how-to-use-the-sapui5-file-upload-feature/ however the code does not seem to execute the reader.onload function? It gets to reader.readAsDataURL(file) and dose not do anything? I am not sure where the problem lies and how to get it to work? Hekp will be much appreciated, there is a similar issue in the blog response but no help has been given.
XML
<u:FileUploader
id="VRCFileUploader"
value="{VRCFileUpload}"
placeholder="Please Attach document"
fileType="jpg,png,pdf"
style="Emphasized"
useMultipart="false" >
</u:FileUploader>
JS
function upload(evnt) {
var token;
var oView = this.getView();
var oFileUploader = this.byId("VRCFileUploader");
var sFileName = oFileUploader.getValue();
if (sFileName === "") {
sap.m.MessageToast.show("Please select a File to Upload");
return;
}
var file = jQuery.sap.domById(oFileUploader.getId() + "-fu").files[0];
var base64_marker = "data:" + file.type + ";base64,";
var reader = new FileReader();
//on load
reader.onLoad = (function(theFile){
return function(evt) {
//locate base64 content
var base64Index = evt.target.result.indexOf(base64_marker) + base64_marker.lenght;
// get base64 content
var base64 = evt.target.result.substring(base64Index);
var sTasksService = "SOME URL";
var sService2 = "SOME URL";
var oViewModel = oView.getModel();
var oContext = oView.getBindingContext();
var oTask = oViewModel.getProperty(oContext.getPath());
var oDataModel = sap.ui.getCore.getModel();
var sWorkitemId = JSON.stringify(oTask.wiId);
var service_url = sService2;
$.ajaxsetup({
cache: false
});
jQuery.ajax({
url: service_url,
asyn: false,
datatype: "json",
cache: false,
data: base64,
type: "post",
beforeSend: function(xhr) {
xhr.setRequestHeader("x-csrf-Token", token);
xhr.setRequestHeader("content-Type", file.type);
xhr.setRequestHeader("slug", sFileName);
xhr.setRequestHeader("WorkitemId", oTask.WiId);
},
success: function(odata) {
sap.m.MessageToast.show("file successfully uploaded");
oFileUploader.setValue("");
},
error: function(odata) {
sap.m.MessageToast.show("file Upload error");
}
});
};
})(file);
//Read file
reader.readAsDataURL(file);
}
In reply to Vortex:
Why is there an IIFE on the method being used on the onLoad Property?
Try to do somenthing like this:
reader.onload = event => {
let fileAsDataUrl = event.target.result;
....
};

Angular 6 Downloading file from rest api

I have my REST API where I put my pdf file, now I want my angular app to download it on click via my web browser but I got HttpErrorResponse
"Unexpected token % in JSON at position 0"
"SyntaxError: Unexpected token % in JSON at position 0↵ at JSON.parse (
this is my endpoint
#GetMapping("/help/pdf2")
public ResponseEntity<InputStreamResource> getPdf2(){
Resource resource = new ClassPathResource("/pdf-sample.pdf");
long r = 0;
InputStream is=null;
try {
is = resource.getInputStream();
r = resource.contentLength();
} catch (IOException e) {
e.printStackTrace();
}
return ResponseEntity.ok().contentLength(r)
.contentType(MediaType.parseMediaType("application/pdf"))
.body(new InputStreamResource(is));
}
this is my service
getPdf() {
this.authKey = localStorage.getItem('jwt_token');
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/pdf',
'Authorization' : this.authKey,
responseType : 'blob',
Accept : 'application/pdf',
observe : 'response'
})
};
return this.http
.get("http://localhost:9989/api/download/help/pdf2", httpOptions);
}
and invocation
this.downloadService.getPdf()
.subscribe((resultBlob: Blob) => {
var downloadURL = URL.createObjectURL(resultBlob);
window.open(downloadURL);});
I resolved it as follows:
// header.component.ts
this.downloadService.getPdf().subscribe((data) => {
this.blob = new Blob([data], {type: 'application/pdf'});
var downloadURL = window.URL.createObjectURL(data);
var link = document.createElement('a');
link.href = downloadURL;
link.download = "help.pdf";
link.click();
});
//download.service.ts
getPdf() {
const httpOptions = {
responseType: 'blob' as 'json')
};
return this.http.get(`${this.BASE_URL}/help/pdf`, httpOptions);
}
I solved the issue in this way (please note that I have merged multiple solutions found on stack overflow, but I cannot find the references. Feel free to add them in the comments).
In My service I have:
public getPDF(): Observable<Blob> {
//const options = { responseType: 'blob' }; there is no use of this
let uri = '/my/uri';
// this.http refers to HttpClient. Note here that you cannot use the generic get<Blob> as it does not compile: instead you "choose" the appropriate API in this way.
return this.http.get(uri, { responseType: 'blob' });
}
In the component, I have (this is the part merged from multiple answers):
public showPDF(fileName: string): void {
this.myService.getPDF()
.subscribe(x => {
// It is necessary to create a new blob object with mime-type explicitly set
// otherwise only Chrome works like it should
var newBlob = new Blob([x], { type: "application/pdf" });
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(newBlob, fileName);
return;
}
// For other browsers:
// Create a link pointing to the ObjectURL containing the blob.
const data = window.URL.createObjectURL(newBlob);
var link = document.createElement('a');
link.href = data;
link.download = fileName;
// this is necessary as link.click() does not work on the latest firefox
link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
setTimeout(function () {
// For Firefox it is necessary to delay revoking the ObjectURL
window.URL.revokeObjectURL(data);
link.remove();
}, 100);
});
}
The code above works in IE, Edge, Chrome and Firefox. However, I don't really like it, as my component is pulluted with browser specific stuff which will surely change over time.
For Angular 12+, I came up with something like this:
this.ApiService
.getFileFromApi()
.pipe(take(1))
.subscribe((response) => {
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(new Blob([response.body], { type: response.body.type }));
const contentDisposition = response.headers.get('content-disposition');
const fileName = contentDisposition.split(';')[1].split('filename')[1].split('=')[1].trim();
downloadLink.download = fileName;
downloadLink.click();
});
The subscribe is on a simple get() with the Angular HttpClient.
// api-service.ts
getFileFromApi(url: string): Observable<HttpResponse<Blob>> {
return this.httpClient.get<Blob>(this.baseApiUrl + url, { observe: 'response', responseType: 'blob' as 'json'});
}
You can do it with angular directives:
#Directive({
selector: '[downloadInvoice]',
exportAs: 'downloadInvoice',
})
export class DownloadInvoiceDirective implements OnDestroy {
#Input() orderNumber: string;
private destroy$: Subject<void> = new Subject<void>();
_loading = false;
constructor(private ref: ElementRef, private api: Api) {}
#HostListener('click')
onClick(): void {
this._loading = true;
this.api.downloadInvoice(this.orderNumber)
.pipe(
takeUntil(this.destroy$),
map(response => new Blob([response], { type: 'application/pdf' })),
)
.subscribe((pdf: Blob) => {
this.ref.nativeElement.href = window.URL.createObjectURL(pdf);
this.ref.nativeElement.click();
});
}
// your loading custom class
#HostBinding('class.btn-loading') get loading() {
return this._loading;
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}
In the template:
<a
downloadInvoice
[orderNumber]="order.number"
class="btn-show-invoice"
>
Show invoice
</a>
My answer is based on #Yennefer's, but I wanted to use the file name from the server since I didn't have it in my FE. I used the Content-Disposition header to transmit this, since that is what the browser uses for a direct download.
First, I needed access to the headers from the request (notice the get method options object):
public getFile(): Observable<HttpResponse<Blob>> {
let uri = '/my/uri';
return this.http.get(uri, { responseType: 'blob', observe: 'response' });
}
Next, I needed to extract the file name from the header.
public getFileName(res: HttpResponse<any>): string {
const disposition = res.headers.get('Content-Disposition');
if (!disposition) {
// either the disposition was not sent, or is not accessible
// (see CORS Access-Control-Expose-Headers)
return null;
}
const utf8FilenameRegex = /filename\*=UTF-8''([\w%\-\.]+)(?:; |$)/;
const asciiFilenameRegex = /filename=(["'])(.*?[^\\])\1(?:; |$)/;
let fileName: string = null;
if (utf8FilenameRegex.test(disposition)) {
fileName = decodeURIComponent(utf8FilenameRegex.exec(disposition)[1]);
} else {
const matches = asciiFilenameRegex.exec(disposition);
if (matches != null && matches[2]) {
fileName = matches[2];
}
}
return fileName;
}
This method checks for both ascii and utf-8 encoded file names, prefering utf-8.
Once I have the file name, I can update the download property of the link object (in #Yennifer's answer, that's the lines link.download = 'FileName.ext' and window.navigator.msSaveOrOpenBlob(newBlob, 'FileName.ext');)
A couple of notes on this code:
Content-Disposition is not in the default CORS whitelist, so it may not be accessible from the response object based on the your server's configuration. If this is the case, in the response server, set the header Access-Control-Expose-Headers to include Content-Disposition.
Some browsers will further clean up file names. My version of chrome seems to replace : and " with underscores. I'm sure there are others but that's out of scope.
//Step: 1
//Base Service
this.getPDF() {
return this.http.get(environment.baseUrl + apiUrl, {
responseType: 'blob',
headers: new HttpHeaders({
'Access-Control-Allow-Origin': '*',
'Authorization': localStorage.getItem('AccessToken') || ''
})
});
}
//Step: 2
//downloadService
getReceipt() {
return new Promise((resolve, reject) => {
try {
// {
const apiName = 'js/getReceipt/type/10/id/2';
this.getPDF(apiName).subscribe((data) => {
if (data !== null && data !== undefined) {
resolve(data);
} else {
reject();
}
}, (error) => {
console.log('ERROR STATUS', error.status);
reject(error);
});
} catch (error) {
reject(error);
}
});
}
//Step 3:
//Component
getReceipt().subscribe((respect: any) => {
var downloadURL = window.URL.createObjectURL(data);
var link = document.createElement(‘a’);
link.href = downloadURL;
link.download = “sample.pdf";
link.click();
});
This also works in IE and Chrome, almost the same answer only for other browsers the answer is a bit shorter.
getPdf(url: string): void {
this.invoiceService.getPdf(url).subscribe(response => {
// It is necessary to create a new blob object with mime-type explicitly set
// otherwise only Chrome works like it should
const newBlob = new Blob([(response)], { type: 'application/pdf' });
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(newBlob);
return;
}
// For other browsers:
// Create a link pointing to the ObjectURL containing the blob.
const downloadURL = URL.createObjectURL(newBlob);
window.open(downloadURL);
});
}

Download zip file using Angular

It has been hours now, since I am trying to figure out how to download a zip file using Angular.
The file downloaded is smaller than the original file. I followed this link How do I download a file with Angular2.
I am not simply using the <a> tag for the download for authentication reason.
service
downloadfile(filePath: string){
return this.http
.get( URL_API_REST + 'downloadMaj?filePath='+ filePath)
.map(res => new Blob([res], {type: 'application/zip'}))
}
component
downloadfileComponent(filePath: string){
this.appService.downloadfile(filePath)
.subscribe(data => this.getZipFile(data)),
error => console.log("Error downloading the file."),
() => console.log('Completed file download.');
}
getZipFile(data: any){
var a: any = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
var blob = new Blob([data], { type: 'application/zip' });
var url= window.URL.createObjectURL(blob);
a.href = url;
a.download = "test.zip";
a.click();
window.URL.revokeObjectURL(url);
}
rest api
public void downloadMaj(#RequestParam(value = "filePath") String filePath, HttpServletResponse response) {
System.out.println("downloadMaj");
File fichierZip = new File(filePath);
try {
System.out.println("nom du fichier:" + fichierZip.getName());
InputStream inputStream = new FileInputStream(fichierZip);
response.addHeader("Content-Disposition", "attachment; filename="+fichierZip.getName());
response.setHeader("Content-Type", "application/octet-stream");
org.apache.commons.io.IOUtils.copy(inputStream, response.getOutputStream());
response.getOutputStream().flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Anyone could tell why all the file is not downloaded?
Solved
downloadfile(filePath: string) {
return this.http
.get( URL_API_REST + 'download?filePath=' + filePath, {responseType: ResponseContentType.ArrayBuffer})
.map(res => res)
}
private getZipFile(data: any) {
const blob = new Blob([data['_body']], { type: 'application/zip' });
const a: any = document.createElement('a');
document.body.appendChild(a);
a.style = 'display: none';
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = test.zip;
a.click();
window.URL.revokeObjectURL(url);
}
In responseType you need to assign a string, in this case, is arraybuffer (Angular 5+)
downloadFile(filename: string) {
return this.http.get(URL_API_REST + 'download?filename=' + filename, {
responseType: 'arraybuffer'
});
}
We can do a window to download directly our file using next code:
this.myService.downloadFile(filename).subscribe(data => {
const blob = new Blob([data], {
type: 'application/zip'
});
const url = window.URL.createObjectURL(blob);
window.open(url);
});
There are multiple plugins you'll need to get zip download working using angular:
angular-file-saver.bundle
This plugin will bundle Blob.js and FileSaver.js
follow all instructions now just add dependencies on your controller and module.
.module('fileSaverExample', ['ngFileSaver'])
.controller('ExampleCtrl', ['FileSaver', 'Blob', ExampleCtrl]);
add JSZip and JSZipUtils
Include files:jszip.js, jszip-utils.js, angular-file-saver.bundle.js
var zip = new JSZip();
zip.file("Hello.txt", "Hello World\n");
// when everything has been downloaded, we can trigger the dl
zip.generateAsync({type:"blob"}).then(function (blob) { // 1) generate the zip file
FileSaver.saveAs(blob, "downloadables.zip"); // 2) trigger the download
}, function (err) {
console.log('err: '+ err);
});
In Angular there is no need of jsZip-util ,you can simple make an service call with header options.
public zipAndDownload(url): Observable<any> {
const options:any = {
headers: new HttpHeaders({'Content-Type': 'file type of an particular document'}),
withCredentials: true,
responseType:'arraybuffer'
};
return this.http.get<Content>(url,options);
}
I use FileSaver
to save files on my local machine. It accepts either blob or string data and saves the file with the given/default name. From the official document:
function FileSaver.saveAs(data: string | Blob, filename?: string, options?: FileSaver.FileSaverOptions): void
Download.Service.ts
downloadFile() {
return this.http.get(url, { params, responseType: 'arraybuffer', observe: 'response' }).pipe(
map(res => res)
);
}
my.component.ts
this.downloadService.downloadFile().subscribe((response: HttpResponse<any>) => {
if(response.body) {
let fileName = "download.zip";
const cDisposition: string = response.headers.get('content-disposition');
if (cDisposition && cDisposition.indexOf(';filename=') > -1) {
fileName = cDisposition.split(';filename=')[1];
}
const data = new Blob([new Uint8Array(response.body)], {
type: 'application/octet-stream'
});
FileSaver.saveAs(data, fileName);
}
})

Mapping List with Knockout Mapping

I have created view model
var catalog = ko.observableArray();
$.ajax({
type: "GET",
url: "http://localhost:8080/ticket-service/rest/ticket/list",
success: function(msg) {
catalog.push.apply(catalog, $.map(msg, function(data) {
return new Ticket(data)
}));
return catalog;
},
error: function(msg) {
console.log(msg)
}
});
and the model
function Ticket(data) {
this.ticketId = ko.observable(data.ticketId);
this.ticketNo = ko.observable(data.ticketNo);
this.ticketTitle = ko.observable(data.ticketTitle);
this.longDescription = ko.observable(data.longDescription);
this.createdBy = ko.observable(data.createdBy);
this.createdOn= ko.observable(data.createdOn);
this.assignTo = ko.observable(data.assignTo);
this.priority = ko.observable(data.priority);
this.dueDate = ko.observable(data.dueDate);
this.status = ko.observable(data.status);
this.projectId = ko.observable(data.projectId);
}
with at the end viewmodel like this
return {
ticket: newTicket,
searchTerm: searchTerm,
catalog: filteredCatalog,
newTicket: newTicket,
addTicket: addTicket,
delTicket: delTicket
};
})();
console.log(vm);
ko.applyBindings(vm);
produce list,add, and delete form.The question is how can i use knockout mapping that can list from get methode.
you need to do something like this
Demonstrated taking a single entity from your code .
view:
Output Preview :
<pre data-bind="text:ko.toJSON($data,null,2)"></pre>
viewModel:
function Ticket(data) {
this.ticketId = ko.observable(data.ticketId);
}
var mapping = {
create: function (options) {
return new Ticket(options.data);
}
};
var ViewModel = function () {
var self = this;
self.catalog = ko.observableArray();
var data = [{
'ticketId': 1
}, {
'ticketId': 2
}]
//under ajax call do the same but pass 'msg' in place of data
self.catalog(ko.mapping.fromJS(data, mapping)())
console.log(self.catalog()); // check console for output
};
ko.applyBindings(new ViewModel());
sample working fiddle here