How to download a .docx file in AngularJs controller from Node.js - javascript

I've been looking in stackoverflow for some answer but I didn't succeed.
I have a node.js method in routes which generates a .docx template from another template with docxtemplater library.
I send a post from angularjs to my /api/generateReport with some data and I generate this .docx but I cant manage to send it.
It is not recommendable nor secure placing the file in /public dir but I can't download it if I place the file in /public dir and I provide the file path to AngularJs.
I've read about blob and other stuff but I can't manage to download a .docx file.
PS: I'm using $resource directive to handle api requests and I have set responseType to arrayBuffer
angular.module('MyApp')
.factory('GenerateReport', function($http, $location,$resource, $rootScope, $alert, $window) {
return $resource("/api/GenerateReport/:id",{}, {
'query': {
method: 'GET',
isArray: false
},
responseType: 'arrayBuffer'
});
});
I send the response this way.
var fileDocx = fs.readFileSync(__base + "/plantillaSalida.docx", "binary");
res.send(fileDocx);
Response is received in angular controller:
GenerateReport.save({
projectExecution: $scope.projectExecution,
auditingProject: $scope.auditingProject,
participants: $scope.participants,
exampleProjects: $scope.exampleProjects
}, function(response) {
/***What to to here??***/
$mdToast.show(
$mdToast.simple()
.content('Informe generado')
.position('bottom right left')
.hideDelay(3000)
);
},
function(error) {
console.log("error");
$mdToast.show(
$mdToast.simple()
.content('Error al general el informe')
.position('bottom right left')
.hideDelay(3000)
);
}
);

I would suggest to add download header to your file and link it using hyperlink (<a href="/download">)
var path = require('path');
var mime = require('mime');
app.get('/download', function(req, res){
var file = __base + "/plantillaSalida.docx";
var filename = path.basename(file);
var mimetype = mime.lookup(file);
res.setHeader('Content-disposition', 'attachment; filename=' + filename);
res.setHeader('Content-type', mimetype);
var filestream = fs.createReadStream(file);
filestream.pipe(res);
});
If you are using express use below code
app.get('/download', function(req, res){
var file = __base + "/plantillaSalida.docx";
var filename = path.basename(file);
res.setHeader('Content-disposition', 'attachment; filename=' + filename);
res.download(file);
});

Related

express detect get request

I'm using electron to build an app which serves several image files in a webserver using express.
From another app built in Android I get files from the server and post files to it.
I have no problems detecting when the Android app is posting the files:
app.post('/fileupload', function(req, res) {
alert("post");
var fstream;
req.pipe(req.busboy);
req.busboy.on('file', function (fieldname, file, filename) {
//console.log("Uploading: " + filename);
fstream = fs.createWriteStream(__dirname + '/images/' + filename);
file.pipe(fstream);
fstream.on('close', function () {
res.redirect('back');
});
});
});
But still have no success detecting when the Android app get the files from the server (it gets them, but I have no way to refresh my output screen when it does), I'm trying with this code:
app.use(function (req, res, next) {
alert("get");
next();
});
and this one too:
app.get('/', function (req, res) {
alert("get");
next();
});
I'm putting the files in a directory called images:
var express = require('express')
var app = express()
app.use(express.static('images'));
app.listen(3000);
EDIT
If I open a browser with the same url Android is getting, it triggers the event and shows the alert. Why it doesn't trigger when Android opens the connection?, I don't know.
The Android code for the get request is:
URL url = new URL(sURL);
HttpURLConnection conection = (HttpURLConnection)url.openConnection();
conection.setRequestMethod("GET");
conection.connect();
int lenghtOfFile = conection.getContentLength();
InputStream inputURL = new BufferedInputStream(url.openStream());
If you are calling API's then you should use res.send() using json
app.post('/fileupload', function(req, res) {
alert("post");
var fstream;
req.pipe(req.busboy);
req.busboy.on('file', function (fieldname, file, filename) {
//console.log("Uploading: " + filename);
fstream = fs.createWriteStream(__dirname + '/images/' + filename);
file.pipe(fstream);
fstream.on('close', function () {
res.send({status:1,data:{}}) //check this response on android side and change/refresh screen on android side if needed
//res.redirect('back');
});
});
});
Using this code, that is deprecated using my compile versión (23), express detects the get request.
HttpClient httpclient = new DefaultHttpClient();
// Prepare a request object
HttpGet httpget = new HttpGet(sURL.substring(0, sURL.lastIndexOf("/")));
// Execute the request
HttpResponse response;
response = httpclient.execute(httpget);

How to send file in a request and download the file within the response without redirecting

I an trying to create a service which takes a xlsx template file and response a populated xlsx file with some values. What I have done so far is,
index.html
<input name="file" type="file" onchange="callthis()"/>
script.js
// callthis sends the file from client to server
function callthis() {
var formData = new FormData($(this).files[0]);
$.ajax({
url: '/uploadTemplate',
type: 'POST',
data: formData,
success: function (data) {
console.log(data);
alert(data)
},
cache: false,
contentType: false,
processData: false
});
}
serverRouter.js
router.post('/uploadTemplate', function(req, res, next){
let uploadedFilePath = null;
// I'm using multer for handling formdata in the server
var upload = multer({ dest: Locator.temp.temp });
//configure the multer
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, Locator.temp.temp);
},
filename: function (req, file, callback) {
uploadedFilePath = file.fieldname + '-' + Date.now() + '.xlsx';
callback(null, uploadedFilePath);
}
});
var upload = multer({ storage : storage}).single('file');
//in here i am uploading the file.
//and reading the file ugin XLSX modules.
//doing some changes to xlsx json object
//and write the data to a file in a temp folder
//i'm using res.download method to send downloadable file back to client
res.download(Path.join(Locator.temp.temp, uploadedFilePath));
});
})
Using above codes, i could upload the file and get response. success method prints out details i added with some unreadable characters. But i could not download the file.
How can i download the file. Are there any different and better approach for this situation.
You can't attach a download to an AJAX request. You will have to send the download URL in the AJAX response and then have your client side script open the URL
In the server:
let response = {downloadUrl: Path.join(Locator.temp.temp, uploadedFilePath)}
res.json(response)
In the client:
window.open(ajaxResponse.downloadUrl);

How to post file content to server using angular file upload and nodejs?

I want to send user selected file to server using angular file-upload i see the data printed in angular service but when i see server controller is printing empty object, Any idea what is implemented wrong here or any better approach to send file to server with content ?
Client
//Controller.js
$scope.uploadFile = function(){
var file = $scope.myFile;
console.log('file is ');
console.dir(file);
var uploadUrl = "/fileUpload";
fileUpload.uploadFileToUrl(file, uploadUrl);
};
// Service.js in service i see data `file` data is printed
angular.module('App').service('fileUpload', ['$http', function ($http) {
this.uploadFileToUrl = function(file, uploadUrl){
/*var fd = new FormData();
fd.append('file', file);
console.log('service called', file);*/
$http.post(uploadUrl,file, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(resp){
})
.error(function(){
});
}
}]);
Server
//routes.js
export default function(app) {
// Insert routes below
app.post('/fileUpload', require('./api/fileUpload'));
}
//index.js
var express = require('express');
var controller = require('./fileUpload.controller');
var router = express.Router();
router.post('/fileUpload',controller.create);
module.exports = router;
//controller.js
export function create(req, res) {
console.log('fileUpload controller server',req.body);
}

How to upload file to s3 through nodejs express from Angularjs

I am facing problem to upload file to s3 by nodejs express, and angularjs.
I am using angular directive to send file to node express and from node to s3.
Angular directive :
(function() {
'use strict';
angular.module('app').directive('ngFileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var model = $parse(attrs.ngFileModel);
var isMultiple = attrs.multiple;
var modelSetter = model.assign;
element.bind('change', function () {
var values = [];
angular.forEach(element[0].files, function (item) {
var value = {
// File Name
name: item.name,
//File Size
size: item.size,
//File URL to view
url: URL.createObjectURL(item),
// File Input Value
_file: item
};
values.push(value);
});
scope.$apply(function () {
if (isMultiple) {
modelSetter(scope, values);
} else {
modelSetter(scope, values[0]);
}
});
});
}
};
}]);
})();
Html code
<input type="file" id="upme" ng-file-model="files" multiple style="display:none;" />
<div ng-if="files.length>0" ng-init="vm.uploadFile()"></div>
Server side:
exports.upload = function(req, res){
var images = req.body.images;
//res.send(images);
// console.dir(images)
images.forEach(function(file){
// console.dir(file);
S3.upFile('testbucket',file.name, file.url, function(err, data){
if(err){
console.log(err)
}else{
console.dir(data);
}
});
});
Problem,
The upload function works and I get something has been uploaded on s3 bucket, the file name appears in bucket; but it seems that is not actual size of the file and I can not open. when I click on the file url it say to download the file, after I download the file, it does not open. I think there may have any problem to parse the file in the node server before uploadin to s3. But I can't identify which solution should be there.
I also get one error in the console.
TypeError: path must be a string or Buffer
at TypeError (native)
at Object.fs.open (fs.js:625:11)
at ReadStream.open (fs.js:1708:6)
at new ReadStream (fs.js:1695:10)
at Object.fs.createReadStream (fs.js:1643:10)
I have made s3 file upload function as module in separate file. Here is module function of file upload
// uploading file or object into bucket
exports.upFile = function(bucket_name, key, file, next){
var params = {Bucket: bucket_name, Key: key, Body: fs.createReadStream(file), ACL:"public-read"};
s3.upload(params, function(err, data) {
next(err, data);
});
};
I appreciate any help from experts.
You are not giving a file as an argument to the upload function but an object URL. To correct your implementation, you have to make some changes in angular. Firstly, you should send files as multipart form data from angular. You can achieve this by using:
var form = new formData();
angular.forEach(element[0].files, function (item) {
form.append('file', item);
});
Send this form data to your node server using http request. You can define the route and http method using express in nodejs. On angularJS the request should look something like this:
$http.post('/test_route', form, {
withCredentials: false,
headers: {
'Content-Type': undefined
},
trnasformRequest: angular.identity
}).success(function(data) {
// do something with data from server
});
At node server, when you receive the request you have to extract files from form data. First define a route and method using express:
var multiparty = require('multiparty');
app.post('test_route', function(req, res) {
var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
var files_to_uplaod = files.file;
files_to_upload.forEach(function(file) {
read_file = fs.readFileSync(file.path);
var params = {Bucket: bucket_name, Key: file.originalFilename, Body: read_file, ACL:"public-read"};
s3.upload(params, function(err, data) {
next(err, data);
// once the file is uploaded you can remove the file from local disk which is saved whn multipart data arrives.
fs.unlink(file.path, function(err) {
if (err) {console.log(err);}
});
});
});
}
});
To parse the multiform on node server, use the multiparty module. More information can be found here: https://www.npmjs.com/package/multiparty

How to prompt save dialog to download file using angularjs?

I have a directory in server that contains file now i am sending file name from client and getting the file from server till that part is working good, below is the response from server now once i receive response i want to prompt for user to download that file for that i am trying to create blob using Angularjs but its not prompting for user to save the file. Any idea ?
ctrl.js
$scope.downloadFile = function(message){
DitFactory.getFile(message).then(function(response){
console.log('r',response);
var blob = new Blob([ response ], { type : 'text/plain' });
$scope.url = (window.URL || window.webkitURL).createObjectURL( blob );
console.log($scope.url);
});
};
serverResponse.json
{"level":"info","message":"another-2fdas message"}
server.js
app.get('/file', function (req, res) {
var dir = './ditLogs';
var root = path.resolve('./ditLogs');
var fileName = req.query.file_name;
var data;
fs.readdir(dir, function(err, items) {
items.forEach(function(file){
if(fileName === file){
data = file;
res.setHeader('Content-Disposition', 'attachment; filename=' + data);
res.sendFile(data, {root: root});
}
});
});
});
If your are using express, you can try below code in server.js file:
var file = 'path to your file';
res.download(file,function(err){
if(!err){
console.log('prompted successfully');
return;
}
});

Categories