Angular get CSV file in browser by calling URL - javascript

I have made a Java micro service to export a table from a database into a CSV file and get it in browser by following this: Downloading a CSV File in Browser by using Java
It works well and I get the file in the browser when I call the URL (by copying and pasting in the browser) :
http://localhost:8080/api/fileDownload/2
In the Angular part when I call the URL in my app I see in the console :
Request URL:http://localhost:8080/api/fileDownload/2
Request Method:GET
Status Code:200
In the preview and the response of the console, I also have the content of my file. So it seems everything looks good but the problem is that my browser doesn't download the file automatically.
My controller :
vm.downloadFunction=function(){
FileDownload.download.query({id:2},function(data){
});
};
My view :
<label data-ng-click="addModule.downloadFunction()" class="btn btn-link btn-file">
Download <input type="button" class="hidden">
</label>
And the service :
voltApp.factory('FileDownload', function ($resource) {
return{
download: $resource('/api/fileDownload/:id', {}, {
query: {method: 'GET', params: {id:'#id'},isArray:false},
update:{method: 'PUT'}
})
};
});
What am I doing wrong?

Consider using ngFileSaver
Example:
JS
function ExampleCtrl(FileSaver, Blob) {
var vm = this;
vm.val = {
text: 'Hey ho lets go!'
};
vm.download = function(text) {
var data = new Blob([text], { type: 'text/plain;charset=utf-8' });
FileSaver.saveAs(data, 'text.txt');
};
}
angular
.module('fileSaverExample', ['ngFileSaver'])
.controller('ExampleCtrl', ['FileSaver', 'Blob', ExampleCtrl]);
HTML
<div class="wrapper" ng-controller="ExampleCtrl as vm">
<textarea
ng-model="vm.val.text"
name="textarea" rows="5" cols="20">
Hey ho let's go!
</textarea>
<a href="" class="btn btn-dark btn-small" ng-click="vm.download(vm.val.text)">
Download
</a>
</div>

Related

angular - #angular/common without NPM

I am trying to use the ng-template directive in my code to be able to show a div after a button click event but I cannot get it to work since I need to have the angular/common library. I tried to reference it by adding a script tag
<script src="https://cdn.jsdelivr.net/npm/#angular/common#11.0.2/bundles/common.umd.min.js" integrity="sha256-+HBVhNZwWCgkN0Z0tvWyjqjm+yI9F/szNt3Yz4/0/ws=" crossorigin="anonymous"></script>
But every time I test my app, the following error appears in the console.
common.umd.min.js:35 Uncaught TypeError: Cannot read property 'InjectionToken' of undefined
I am stuck and don't know how can I be able to use the ng-template directive
HTML (index.html):
<ng-template>
<div class="container">
<h2>Edit or Delete an Evangelist</h2>
<form name="userEditForm">
<p>person id: <input type="text" id="name" ng-model="userEdit.personid" disabled /></p>
<p>name: <input type="text" id="name" ng-model="userEdit.name" /></p>
<p>location: <input type="text" id="location" ng-model="userEdit.location" /></p>
<button id="btn-edit-evangelist" class="btn btn-primary" ng-click="editName(userEdit)">Save</button>
<button id="btn-canceledit-evangelist" class="btn btn-default btn" ng-click="delName(userEdit);">Delete User</button>
</form>
</div>
<div *ngIf="isEdit">
</div>
</ng-template>
Angular (main-app.js):
"use strict";
angular.module('MainApp', [
])
.controller("MainController", function ($scope, $http) {
//initialize scope variables
$scope.user = {
name: function (theName) {
if (angular.isDefined(theName)) {
$scope._name = theName;
}
return $scope._name;
}(),
location: function (theLocation) {
if (angular.isDefined(theLocation)) {
$scope._location = theLocation;
}
return $scope._location;
}()
};
function redirectToEdit(user) {
this.isEdit = !this.isEdit;
var id = user.personid;
$http.get('https://webapimongodb.herokuapp.com/api/name/' + id, {
params: { personid: user.personid, name: user.name, location: user.location },
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
})
.success(function (res) {
console.log(res);
$scope.userEdit.personid = res.personid;
$scope.userEdit.name = res.Name;
$scope.userEdit.location = res.Location;
});
}
})
Will it be possible to have the #angular/common module referenced via a script tag or do I need to have it referenced via NPM?
I think the issue here is the version being used.
The code you provided is written in Angular.js code and #angular/common is an Angular module
To get your code working you need this instead
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.0/angular.min.js"></script>
Here you can find the Angular.js docs, which, by the way, is deprecated.
And this article explains the difference among Angular and Angular.js

Current request is not a multipart request

I'm trying to send an image to my server. I'm keep getting the error: Current request is not a multipart request. When i test it in Postman it works fine.
This is my html form:
function saveImageToProduct() {
var formData = new FormData(document.querySelector("#newImagesForm"));
var encData = new URLSearchParams(formData.entries());
fetch("/uploadFile", { method: 'POST', body: encData })
.then(response => Promise.all([response.status, response.json()]))
.then(function([status, myJson]) {
if (status == 200) {
console.log("succeed!");
} else {
console.log("failed!");
}
})
.catch(error => console.log(error.message));
return false;
}
<form enctype="multipart/form-data" novalidate="novalidate" id="newImagesForm" method="post">
<div>
<p>Selecteer een afbeelding:</p>
<input id="file" name="file" type="file"/>
</div>
<br>
<div>
<button id="button" onclick="return saveImageToProduct()" class="btn btn-lg btn-info btn-block">
<span>Voeg aanbieding toe</span>
</button>
</div>
</form>
Backend Java code:
#PostMapping("/uploadFile")
public ProductImage uploadFile(#RequestParam("file") MultipartFile file) {
String fileName = fileStorageService.storeFile(file);
String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/uploads/")
.path(fileName)
.toUriString();
return new ProductImage(fileName, fileDownloadUri,
file.getContentType(), file.getSize());
}
When i try to send the image i'm getting a 500 error in the backend:
2019-03-10 19:40:33.588 ERROR 5668 --- [io-9001-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Current request is not a multipart request] with root cause org.springframework.web.multipart.MultipartException: Current request is not a multipart request
When i do it in Postman it works fine like the following image shows:
Any idea what i'm doing wrong here? Thanks in advance
The code below should do the job:
You basically create a new Form object and append the file data to it.
You are able to add multiple data attributes to it by adding more "data.append" lines.
function uploadPicture() {
var input = document.querySelector('input[type="file"]')
console.log(productID);
var data = new FormData()
data.append('file', input.files[0])
fetch('/uploadFile/', {
method: 'POST',
body: data
})
.then(response => Promise.all([response.status, response.json()]))
.then(function([status, myJson]) {
if (status == 200) {
console.log("succeed!");
} else {
console.log("failed!");
}
})
.catch(error => console.log(error.message));
}
HTML:
<input type="file" name="file" id="fileinput">
<input type="submit" value="Upload" onclick="uploadPicture()">
You can try modifying it -
var formData = new FormData(document.querySelector("#newImagesForm")[0]);

$http.get unable to fetch data from SpringBoot controller

I am creating an application which will run queries on my store's database, based on what the user enters on the webpage. I have successfully created the backend method. And it successfully returns the response. But I am unable to retrieve the data and display it on my webpage in the form of a dynamic table. I am a bit new to AngularJS, so please bear with me, but any help is appreciated.
StoreController.java
#RequestMapping(value = "/runQuery", method = RequestMethod.GET)
public List<Map<String, Object>> runQuery(#RequestParam(value="query", defaultValue="* FROM items") String statement, Model model) {
List<Map<String, Object>> answer = storeService.executeUserQuery(statement);
model.addAttribute("resultList", answer);
return answer;
}
I tried to model my controller in such a way that it can dynamically take the data received from the Java controller and assign it to the $scope variable.
app.module.js
(function(){
'use strict';
angular.module('app', []);
})();
store.controller.js
angular
.module('app').controller('StoreController', ['$scope','StoreService','StoreController','$q', function ($scope,StoreService, StoreController, $q) {
$scope.runQuery = function () {
StoreService.runQuery($scope.statement)
.then (function (data){
$scope.rows = response.data;
$scope.cols = Object.keys($scope.rows[0]);
},
function error(response){
if (response.status == 404){
$scope.errorMessage = response.data[0];
}
else {
$scope.errorMessage = 'Error displaying result user!';
}
});
}
}
]);
app.service('StoreService',['$http', function ($http,$q) {
this.runQuery = function runQuery(statement){
return $http({
method: 'GET',
url: 'http://localhost:8080/runQuery/',
params: {statement:statement},
headers: 'Accept:application/json'
}).then( function(response){
return reponse.data;
});
}
index.html
<body data-ng-app="app" data-ng-controller="StoreController">
<div class="container">
<form th:action="#{/logout}" method="get">
<button class="btn btn-md btn-danger btn-block"
style="color: #fff; background-color: #e213a2; border-color: #c3c2c0;"
name="registration" type="Submit">Logout</button>
</form>
<div class="panel-group" style="margin-top: 40px">
<div class="panel panel-primary">
<div class="panel-heading">
<span th:utext="${userName}"></span>
</div>
<div>
<form name="queryForm" method="get" data-ng-submit="runQuery()">
<div class="panel-body">
<h3 id="queryLabel">Select Query:</h3>
<textarea id="query" wrap="soft"
placeholder="Please do not enter SELECT with your query, it's added automatically!!" data-ng-model="statement"></textarea>
<button type="submit">Run Query</button>
</div>
</form>
<div class="panel-body" id="results">
<h3 id="queryLabel">Result:</h3>
<table border="1">
<tr>
<th data-ng-repeat="column in cols">{{column}}</th>
</tr>
<tr data-ng-repeat="row in rows">
<td data-ng-repeat="column in cols">{{row[column]}}</td>
</tr>
</table>
</div>
</div>
<div>
<p class="admin-message-text text-center" th:utext="${adminMessage}"></p>
</div>
</div>
</div>
</div>
</body>
The table on the html page, works because I received it from this link
http://jsfiddle.net/v6ruo7mj/1/
But it's not populating the tables with the data received from my backend controller method. I do not have any entities as this is just querying an existing database, so I need not to add any entities.
The issue probably is this line here in the service callback within your controller:
.then (function (data){
$scope.rows = response.data;
// ...
}
try with:
.then (function (data){
$scope.rows = data;
// ...
}
You already return the responses data in your service when calling:
}).then( function(response){
return reponse.data;
});
Aside from your question I should mention that your Spring controller seems to be vunerable to SQL injection. It's in general not a good idea to allow the user to access your database directly. Although I don't know how your StoreService on the backend is implemented. But it seems as if an attacker could easily send a HTTP call to your endpoint and drop your database.
You have a typo in the runQuery function:
app.service('StoreService',['$http', function ($http,$q) {
this.runQuery = function runQuery(statement){
return $http({
method: 'GET',
url: 'http://localhost:8080/runQuery/',
params: {statement:statement},
headers: 'Accept:application/json'
}).then( function(response){
̶r̶e̶t̶u̶r̶n̶ ̶ ̶r̶e̶p̶o̶n̶s̶e̶.̶d̶a̶t̶a̶;̶
return response.data
});
}
}]);

Multiple file upload with angular being deleted before sending files

I'm trying to upload multiple files to server.
The file is being stored in a variable.
When I click upload the client is sending that variable empty.
Here is my html
<div class = "control-group form-group col-md-12"
ng-class = "{'has-error':newAsset.OSGImages.$invalid && newAsset.OSGImages.$dirty}" ng-if = "showUploadFile(4)">
<label>OSG Images (max 3 MB)</label>
<input type="file" multiple ngf-select="" ng-model="newAssetForm.OSGImages" name = "OSGImages" ngf-max-size="3 MB">
<p ng-repeat="image in newAssetForm.OSGImages">
{{image.name}}
</p>
<span class = "help-block has-error" ng-if="newAsset.OSGImages.$dirty">
</span>
</div>
Here is my js code:
enter code var fileReader = new FileReader();
fileReader.readAsArrayBuffer($scope.newAssetForm.OSGImages[0]);
fileReader.onload = function(e) {
console.log(e);
Upload.upload({
url: '/api/uploadAsset',
headers: {
'Content-Type': 'multipart/form-data'
},
data: {
'projectId': $scope.projectId,
'loadValueType' : $scope.newAssetForm.loadValueType,
'outputVideoLink': $scope.newAssetForm.outputVideoLink,
'isActive': $scope.newAssetForm.isActive,
'PImageSet': PImageSet,
inputImageUrl: $scope.newAssetForm.inputImageUrl,
markerPattFile: $scope.newAssetForm.markerPattFile,
cinema: $scope.newAssetForm.cinema,
objFile: $scope.newAssetForm.objFile,
mtlFile: $scope.newAssetForm.mtlFile,
PImage: $scope.newAssetForm.PImage,
osgFile: $scope.newAssetForm.OSGFile,
osgImage: e.target.result,
},
})
.then(function onSuccess(result){
$scope.$emit('create_asset', result.data);
$modalInstance.close();
})
I tried uploading with postman and the server is getting the files.
Also, the console logs print the files correctly.
Why the client is sending empty variable?

Angularjs how to upload multipart form data and a file?

I'm a beginner to angular.js but I have a good grasp of the basics.
What I am looking to do is upload a file and some form data as multipart form data. I read that this isn't a feature of angular, however 3rd party libraries can get this done. I've cloned angular-file-upload via git, however I am still unable to post a simple form and a file.
Can someone please provide an example, html and js of how to do this?
First of all
You don't need any special changes in the structure. I mean: html input tags.
<input accept="image/*" name="file" ng-value="fileToUpload"
value="{{fileToUpload}}" file-model="fileToUpload"
set-file-data="fileToUpload = value;"
type="file" id="my_file" />
1.2 create own directive,
.directive("fileModel",function() {
return {
restrict: 'EA',
scope: {
setFileData: "&"
},
link: function(scope, ele, attrs) {
ele.on('change', function() {
scope.$apply(function() {
var val = ele[0].files[0];
scope.setFileData({ value: val });
});
});
}
}
})
In module with $httpProvider add dependency like ( Accept, Content-Type etc) with multipart/form-data. (Suggestion would be, accept response in json format)
For e.g:
$httpProvider.defaults.headers.post['Accept'] = 'application/json, text/javascript';
$httpProvider.defaults.headers.post['Content-Type'] = 'multipart/form-data; charset=utf-8';
Then create separate function in controller to handle form submit call.
like for e.g below code:
In service function handle "responseType" param purposely so that server should not throw "byteerror".
transformRequest, to modify request format with attached identity.
withCredentials : false, for HTTP authentication information.
in controller:
// code this accordingly, so that your file object
// will be picked up in service call below.
fileUpload.uploadFileToUrl(file);
in service:
.service('fileUpload', ['$http', 'ajaxService',
function($http, ajaxService) {
this.uploadFileToUrl = function(data) {
var data = {}; //file object
var fd = new FormData();
fd.append('file', data.file);
$http.post("endpoint server path to whom sending file", fd, {
withCredentials: false,
headers: {
'Content-Type': undefined
},
transformRequest: angular.identity,
params: {
fd
},
responseType: "arraybuffer"
})
.then(function(response) {
var data = response.data;
var status = response.status;
console.log(data);
if (status == 200 || status == 202) //do whatever in success
else // handle error in else if needed
})
.catch(function(error) {
console.log(error.status);
// handle else calls
});
}
}
}])
<script src="//unpkg.com/angular/angular.js"></script>
This is pretty must just a copy of that projects demo page and shows uploading a single file on form submit with upload progress.
(function (angular) {
'use strict';
angular.module('uploadModule', [])
.controller('uploadCtrl', [
'$scope',
'$upload',
function ($scope, $upload) {
$scope.model = {};
$scope.selectedFile = [];
$scope.uploadProgress = 0;
$scope.uploadFile = function () {
var file = $scope.selectedFile[0];
$scope.upload = $upload.upload({
url: 'api/upload',
method: 'POST',
data: angular.toJson($scope.model),
file: file
}).progress(function (evt) {
$scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total, 10);
}).success(function (data) {
//do something
});
};
$scope.onFileSelect = function ($files) {
$scope.uploadProgress = 0;
$scope.selectedFile = $files;
};
}
])
.directive('progressBar', [
function () {
return {
link: function ($scope, el, attrs) {
$scope.$watch(attrs.progressBar, function (newValue) {
el.css('width', newValue.toString() + '%');
});
}
};
}
]);
}(angular));
HTML
<form ng-submit="uploadFile()">
<div class="row">
<div class="col-md-12">
<input type="text" ng-model="model.fileDescription" />
<input type="number" ng-model="model.rating" />
<input type="checkbox" ng-model="model.isAGoodFile" />
<input type="file" ng-file-select="onFileSelect($files)">
<div class="progress" style="margin-top: 20px;">
<div class="progress-bar" progress-bar="uploadProgress" role="progressbar">
<span ng-bind="uploadProgress"></span>
<span>%</span>
</div>
</div>
<button button type="submit" class="btn btn-default btn-lg">
<i class="fa fa-cloud-upload"></i>
<span>Upload File</span>
</button>
</div>
</div>
</form>
EDIT: Added passing a model up to the server in the file post.
The form data in the input elements would be sent in the data property of the post and be available as normal form values.
It is more efficient to send the files directly.
The base64 encoding of Content-Type: multipart/form-data adds an extra 33% overhead. If the server supports it, it is more efficient to send the files directly:
Doing Multiple $http.post Requests Directly from a FileList
$scope.upload = function(url, fileList) {
var config = {
headers: { 'Content-Type': undefined },
transformResponse: angular.identity
};
var promises = fileList.map(function(file) {
return $http.post(url, file, config);
});
return $q.all(promises);
};
When sending a POST with a File object, it is important to set 'Content-Type': undefined. The XHR send method will then detect the File object and automatically set the content type.
Working Demo of "select-ng-files" Directive that Works with ng-model1
The <input type=file> element does not by default work with the ng-model directive. It needs a custom directive:
angular.module("app",[]);
angular.module("app").directive("selectNgFiles", function() {
return {
require: "ngModel",
link: function postLink(scope,elem,attrs,ngModel) {
elem.on("change", function(e) {
var files = elem[0].files;
ngModel.$setViewValue(files);
})
}
}
});
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<h1>AngularJS Input `type=file` Demo</h1>
<input type="file" select-ng-files ng-model="fileList" multiple>
<h2>Files</h2>
<div ng-repeat="file in fileList">
{{file.name}}
</div>
</body>
You can check out this method for sending image and form data altogether
<div class="form-group ml-5 mt-4" ng-app="myApp" ng-controller="myCtrl">
<label for="image_name">Image Name:</label>
<input type="text" placeholder="Image name" ng-model="fileName" class="form-control" required>
<br>
<br>
<input id="file_src" type="file" accept="image/jpeg" file-input="files" >
<br>
{{file_name}}
<img class="rounded mt-2 mb-2 " id="prvw_img" width="150" height="100" >
<hr>
<button class="btn btn-info" ng-click="uploadFile()">Upload</button>
<br>
<div ng-show = "IsVisible" class="alert alert-info w-100 shadow mt-2" role="alert">
<strong> {{response_msg}} </strong>
</div>
<div class="alert alert-danger " id="filealert"> <strong> File Size should be less than 4 MB </strong></div>
</div>
Angular JS Code
var app = angular.module("myApp", []);
app.directive("fileInput", function($parse){
return{
link: function($scope, element, attrs){
element.on("change", function(event){
var files = event.target.files;
$parse(attrs.fileInput).assign($scope, element[0].files);
$scope.$apply();
});
}
}
});
app.controller("myCtrl", function($scope, $http){
$scope.IsVisible = false;
$scope.uploadFile = function(){
var form_data = new FormData();
angular.forEach($scope.files, function(file){
form_data.append('file', file); //form file
form_data.append('file_Name',$scope.fileName); //form text data
});
$http.post('upload.php', form_data,
{
//'file_Name':$scope.file_name;
transformRequest: angular.identity,
headers: {'Content-Type': undefined,'Process-Data': false}
}).success(function(response){
$scope.IsVisible = $scope.IsVisible = true;
$scope.response_msg=response;
// alert(response);
// $scope.select();
});
}
});

Categories