Angular: how to download file without showing access_token in url - angularjs

My web application is secured by Oauth2. For ajax calls, the access_token has to be provided in the request folder. For example, here is one of the methods in factory.js:
service.getAll = function () {
var url = SERVER + "/ristore/foundation/";
return $http({
headers: {'Authorization': 'Bearer ' + $window.localStorage.getItem("access_token")},
url: url,
method: 'GET',
crossOrigin: true
})
}
Now I would like to download a file from the webpage. The file is passed to client from server with streaming:
#RequestMapping(
value = "/ristore/foundation/xml/{filename}",
method = RequestMethod.GET,
produces = "application/xml")
public ResponseEntity<byte[]> downloadXMLFile(#PathVariable String filename) throws IOException {
FileSystemResource xmlFile = new FileSystemResource("/rsrch1/rists/moonshot/data/prod/foundation/xml/" + filename + ".xml");
byte [] content = new byte[(int)xmlFile.contentLength()];
IOUtils.read(xmlFile.getInputStream(), content);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream"))
.contentLength(xmlFile.contentLength())
.body(content);
}
In html, I specify the ng-click() on the button that will be clicked to start downloading:
<td data-title="'File'" class="text-center"><span class="glyphicon glyphicon-download-alt" ng-click="download(report.filename)"></span></a>
</td>
Based on the answer to this post Download files in Javascript with OAuth2, $window.open is used to handle the url in my controller:
$scope.download = function(filename) {
var url = "http://rcdrljboss01a:9880/ristoreService/ristore/foundation/xml/" + filename + "?access_token=" + $window.localStorage.getItem("access_token");
$window.open(url);
}
I am able to download file this way, but the access_token is shown in the downloading url. Is there a way to hide the access_token in the url?

You should put the access token in the header instead of in the query parameter.
This tutorial shows how this works in detail.
Edit : Is there a way to add the token to the header of wondow.open(..) ?
No, it seems not to be possible to directly change the header for window.open(..)
What you can do :
Get the xml with ajax, open a new window and set the file as content of the window (pseudo code, you may need to convert the content to string):
var win = open('some-url','windowName','height=300,width=300');
win.document.write(content);

Related

Downloaded document getting corrupted using Blob method in angularJS

Downloading a file used to work fine in my application until I upgraded Angular to the latest. Even now, the file is getting downloaded, but the issue is that it is getting corrupted. Upload file is working fine and if we check in the file server, the file will be intact. But upon download, I am getting corrupted file.
Html :
<td data-title="''">
<a tooltip="Download CV" ng-hide="!talent.resumePath" tooltip-trigger tooltip-animation="false" tooltip-placement="bottom" ng-click="downloadResume(talent.id)" data-placement="top" data-toggle="tooltip" data-original-title="resume">
<img src="../../img/DownloadIcon.png" /></a>
</td>
Controller :
downloadResume: function(employeeId) {
return apiServices.getFileFromTalentPool('/talentpool/resume?id=' + employeeId)
},
Where, getFileFromTalentPool is : https://hastebin.com/yivaterozi.js
Endpoint :
public FileResult GetResume(int id) {
var result = _services.GetResume(id);
if (result != null) {
HttpContext.Response.ContentType = result.ContentType;
HttpContext.Response.Headers["Access-Control-Expose-Headers"] = "FileName";
HttpContext.Response.Headers["FileName"] = result.FileDownloadName;
}
return result;
}
Usually I download Doc files. I tried with a notepad file to see if it's the same. Strangely, I noticed that I am able to open the notepad file, but its content is manipulated to something like [object Object]. But for Doc files, it just shows:
How can I fix this?
it looks like the code at https://hastebin.com/yivaterozi.js was updated from using deprecated $http.success() method to current $http.then(). Promise' success callback function (within then method) receives only one object argument: https://docs.angularjs.org/api/ng/service/$http. Deprecated 'success' method got more arguments (data, status, headers) and data already contained raw data. When using then(), data is located under data property of response, so try to change your $http call to:
$http({
method: 'GET',
cache: false,
url: fileurl,
responseType:'arraybuffer',
headers: {
'Authorization': "Bearer " + $rootScope.userInfo.access_token,
'Access-Control-Allow-Origin': '*'
}
}).then(function (data) {
var octetStreamMime = 'application/octet-stream';
var success = false;
// Get the headers
var headers = data.headers();
...
...
please note that headers are fetched correct here from the data object and not from the third argument (just add var, since we removed empty arguments).
Now in each place that you use data, change it to data.data, like:
// Try using msSaveBlob if supported
var blob = new Blob([data.data], { type: contentType });
or just change argument data to response and add var data = response.data; anf modify headers getter to headers = response.headers();:
$http({
method: 'GET',
cache: false,
url: fileurl,
responseType:'arraybuffer',
headers: {
'Authorization': "Bearer " + $rootScope.userInfo.access_token,
'Access-Control-Allow-Origin': '*'
}
}).then(function (response) {
var octetStreamMime = 'application/octet-stream';
var success = false;
// Get data
var data = response.data;
// Get the headers
var headers = response.headers();
...
...

How to successfully embed blob PDF response in IE11/Edge

I am not able to embed a blob url as an PDF in IE 11/Edge. There is a CORS issue and IE gives an 'Access Denied'. From my research on SO, I have realized that this is due to IE's inherent security restrictions. My question is is there any other way of taking the blob URL data response from the REST service and displaying it embedded in the browser. I want to avoid using any third party libraries.
The service returns as below:
function getTest(id) {
miEService.get(id)
.then(function (response) {
var fileUrl = URL.createObjectURL(response.data);
$scope.pdfData = $sce.trustAsResourceUrl(fileUrl);
});
get: function (id) {
var config = {
headers: {
accept: 'application/pdf'
}
, responseType: 'blob'
}
return $http.get(miEnv.services.eApi + '?$filter=id eq' +
' ' + id, config);
Finally inside the html the display is as below -
<object id="pdf" data={{$ctrl.pdfData}} type="application/pdf" width="100%" height="100%" alt="pdf" class="view-pdf_document">
If the rest service returns the binary data of the pdf, you could just embedd the url in your page as an Iframe rather than the binary content itself. It should render the pdf. If not, I've had success in the past just having the pdf be a link and when you click the link it opens the request to the rest service in a new tab. If the issue is that you don't want the rest service to show up in the url on the page, then you might have to proxy the request through your own server.

Download MS Excel/Word via AngularJS call to a REST service

I have an AngularJS application that sends a POST request to a REST service (onClick method of a button). The POST request contains a JSON object with various settings. The REST service uses those settings to create a MS Word/Excel file.
At the moment the REST service sends the contents of the file back as a byte stream (in response to the previously mentioned POST request). When the file arrives I want a save-file-dialog to show up, where I can save the file. The backend is a Spring Boot app using Spring-MVC.
Can this be done in AngularJS?
If you can't use something like location.href to get your data to the server instead of post it, then check it out others using html 5:
more info AngularJS $http-post - convert binary to excel file and download
$http({
url: 'your/webservice',
method: 'POST',
responseType: 'arraybuffer',
data: json, //this is your json data string
headers: {
'Content-type': 'application/json',
'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}
}).success(function(data){
var blob = new Blob([data], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
saveAs(blob, 'File_Name_With_Some_Unique_Id_Time' + '.xlsx');
}).error(function(){
//Some error log
});
This is the Controller function I ended up using:
#RequestMapping(value = "/downloadDocx", method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.CREATED)
public void downloadDocx(#RequestBody DocxInputBean docxInput,
HttpServletResponse response) throws Exception {
File docxFile = outputManager.createDocxProfile(docxInput);
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
InputStream is = new FileInputStream(docxFile);
org.apache.commons.io.IOUtils.copy(is, response.getOutputStream());
response.flushBuffer();
is.close();
}

Open a PDF in a new window of the browser with angularjs

I'm new to angular js and I wish to open a PDF document in a new window of the browser after pressing a button.
I make a GET request with $http.get() at front end, at backend there is a Java rest service that respond to the GET and generates a PDF. I wish to open this PDF on the browser.
If is not possible to open the PDF in this way then at least open any PDF with AngularJs, how could I do this?
#GET
#Path("/printPdf")
public Response printService(){
//generates the pdf
File reportFile = new File(filePath);
String name = reportName + "." + "pdf";
ResponseBuilder response = Response.ok(new TemporaryFileInputStream(reportFile));
response.header("Content-Disposition", "attachment; filename=" + name);
response.header("Content-Type", "application/pdf");
response.header("Access-Control-Expose-Headers", "x-filename");
response.header("x-filename", name);
return response.build();
}
this is what there is at backend to generate the response in the rest service.
If you had something like this:
var myPdfUrl = 'something'
$http.get(myPdfUrl);
Do this instead:
var myPdfUrl = 'something'
$window.open(myPdfUrl);
If instead you have something like this:
$http
.get(generatePdfUrl)
.then(function(data){
//data is link to pdf
});
Do this:
$http
.get(generatePdfUrl)
.then(function(data){
//data is link to pdf
$window.open(data);
});
Maybe this can help,in the case of you have something like this :
$http.get('generatePdfUrl')
.then(function (data) { // data is your url
var file = new Blob([data], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
});
Then use your service in controller and do
$window.open(fileURL);

Upload image to server angularjs

I need to upload an image taken from my mobile device to my server. I found the angular-upload library to which makes reference. I need to do is to transform the image base 64, send it by post to my server because the server is where I will work with her. And the other, send from my server and work it from the application to run.
var server = URL_BASE+'addView/';
var trustAllHosts = true;
var ftOptions = new FileUploadOptions();
ftOptions.fileKey = 'file';
ftOptions.fileName = $scope.imagen.substr($scope.imagen.lastIndexOf('/') + 1);
ftOptions.mimeType = 'image/jpeg';
ftOptions.httpMethod = 'POST';
console.log(ftOptions);
$cordovaFileTransfer.upload(encodeURI(server), $scope.imagen, ftOptions, trustAllHosts)
.then(function(result) {
console.log(result)
}, function(err) {
// Error
console.log(err);
}, function (progress) {
});
ionic file transfer
I'm personally using Cordova file transfer for upload & download content from a server.
Base64 encoding
Don't know where is your image stored and how you retrieve it, but, either you specify that the image is base64 encode into the HTML file delimiter
OR
You transform your image using a canvas
See that post for more info : https://stackoverflow.com/a/20285053/3687474
You haven't specified what you really need so:
Here you have a factory
//Factory you register on your module
angular
.module('myApp')
.factory('sendBase64Image', sendBase64Image)
function sendBase64Image($http) {
var urlBase; //url to be filled in
var base64 = {};
base64.sendBase = function (baseImg) {
var request = $http({
method: "post",
url: urlBase,
headers: {
'Content-type': 'application/json'
},
data : baseImg
});
}
return base64;
}
You should then inject it via dependency injection to your controller and perform call to the server.
If you want to do something with a response use success() method to handle promise response.

Resources