I am having json data, i want to export that as data.xlsx. For example
I have table there is a export button, if user click that export button, this ng-click function will work.
controllers.js:
$scope.exportDataXlsx = function () {
var json = $scope.contacts;
console.log(json);
$http.post('/api/exportcontactsxlsx', json).then(function (response) {
$state.reload();
toastr.success("exported successfully!");
});
};
My api code:
exports.exportContactsXlsx = function (req, res) {
var data = req.body;
var xls = json2xls(data);
fs.writeFileSync('data.xlsx', xls, 'binary');
}
I am using npm package called jsonexport.
If i click export, the file will downloaded to my project.
But i need output, when user click export button The 'data.xlsx' file should be downloaded in chrome left corner, and in user default download directory.
after saving your file in your server. what you need is to send the name of your file so you can access it from the browser :
$scope.exportDataXlsx = function () {
var json = $scope.contacts;
console.log(json);
$http.post('/api/exportcontactsxlsx', json).then(function (response) {
downloadFile(response.fileName) // make sure response.fileName has the file name
$state.reload();
toastr.success("exported successfully!");
});
};
function downloadFile(name) {
var link = document.createElement('a');
link.download = name;
link.href = '/files/' + name;
link.click();
}
server :
// in app.js, to serve your files as static files
app.use("/files", express.static(path.join(__dirname, 'files')));
// send the the name of your file to the client
exports.exportContactsXlsx = function (req, res) {
var data = req.body;
var xls = json2xls(data);
fs.writeFileSync(path.join(__dirname,'../files/data.xlsx'), xls, 'binary');
//changed the directory
res.json({ fileName: 'data.xlsx' });
}
You have to set parametrs in the res headers.
You can try for the following:
var fields = ['firstName', 'email'];
var csv = json2csv({ data: resp, fields: fields });
res.set('Cache-Control', 'max-age=0, no-cache, must-revalidate, proxy-revalidate');
res.set('Content-Type','application/force-download');
res.set('Content-Type','application/octet-stream');
res.set('Content-Type','application/download');
res.set('Content-Disposition','attachment;filename=userList.csv');
res.set('Content-Transfer-Encoding','binary');
res.send(csv);
SO when you hit the API in browser, it will ask for SaveFile option and if user clicks OK it will be downloaded to default Download directory of chrome.
Related
So the file uploaded is an excel file that sheetJS needs to read, otherwise it will show as {}.
app.post('/sendExcel', function(req, res) {
let data = req.body;
var workbook = sheetJS.read(data, {type: 'buffer'});
console.log(workbook.Sheets['Sheet1); //prints... "{ A1: { t: 's', v: '[object Object]' }, '!ref': 'A1' }"
let excel = workbook.Sheets['Sheet1']['A1']['v'][0]; //prints... "["
So I've tried various things including changing the type client side as I had problems with it being of type buffer. So now it works partially, but I still can't access the data in the sheet.
As an example, I used the file path instead here, and it's shown to work as normal.
app.get('/excel', function(err, res, data) {
var wb = sheetJS.readFile("data.xlsx");
let excel = wb.Sheets['Sheet1']['A1']['v'];
console.log(excel); //this prints "vehicle", which is what is supposed to happen, not "[".
res.send(excel)
});
I am supposed to get the excel data from the form upload. That's the issue. It is is now successful when sending to the db, but will not access the whole data. I believe I need to change it back to an array.
You can use:
var fileReader = new FileReader();
fileReader.readAsArrayBuffer(workbook);
But this will not run in app.js
Here is my other answer with client-side and server-side. It might be helpful to others.
Javascript Read Excel file on server with SheetJS
Don't use the file reader. Append the excel sheet to the form in the body normally.
Client side:
let excelInput = document.getElementById("fileToUpload");
//excelInput: this html element allows you to upload the excel sheet to it
let excelFile = excelInput.files[0];
let form = new FormData();
form.append("excel", excelFile);
fetch('/sendExcel', {method: "POST", body: form})
.then((data) => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
Then use formidable server side.
Server side:
const sheetJS = require('xlsx');
const formidable = require('formidable');
app.post('/excel', function(req, res) {
let data = req.body;
const form = formidable({ multiples: true });
form.parse(req, (err, fields, files, next) => {
if (err) {
next(err);
return;
}
var f = files[Object.keys(files)[0]];
var workbook = sheetJS.readFile(f.path);
res.send(workbook);
});
});
So formidable has to be used otherwise it won't work. Then you can use sheetJS.readFile instead of sheetJS.read.
I'm developing a chatbot using Microsoft Bot Framework with Node Js.
My purpose is to send the user a csv file while he asks me something.
I've implemented the following function but while I download the file 'prova.csv' the format is not recognized.
Inspecting well the format in output is of the type: "data"
Anyone can help me what's wrong? Thanks
function (session, results, next) {
if (results.response.entity === 'Si') {
const contentType = 'text/csv';
const response = session.dialogData.far_ric_formato.response.rows;
const csv = response.map(ric => `${ric.num};${ric.pin}`).join('\n');
session.send({
text: 'Ecco il CSV pronto per il download (MOCK)',
attachments: [
{
contentType: contentType,
contentUrl: `data:${contentType};base64,${Buffer.from(csv).toString('base64')}`,
name: 'prova.csv'
}
]
});
Base64 string of a .csv file can't be directly rendered on client side, as a workaround you can for example code like this:
var restify = require('restify');
var fs = require('fs');
bot.dialog('download', (session, result)=>{
fs.readFile('./files/test.csv', function(err, data){
var contentType = 'text/csv';
var base64 = Buffer.from(data).toString('base64');
var msg = new builder.Message(session)
.addAttachment({
contentUrl: 'http://localhost:3978/csv/'+base64, //replace with your server url + base64 string.
contentType: contentType,
name: 'MyTest.csv',
});
session.send(msg);
});
}).triggerAction({matches:/^download/i});
server.get('/csv/:base64code', (req, res, next)=>{
let base64code = req.params.base64code;
res.header('Content-disposition', 'inline; filename=test.csv');
res.header('Content-type', 'application/csv');
res.send(Buffer.from(base64code, 'base64'));
});
When user trigger the download dialog, it will send this file as attachment and when user click on this file, this .csv file will be downloaded in user's client side.
I'm creating a temporary JSON file in my NodeJS backend which holds the information the user has filled in a form. At the end of the form when user clicks on the download button, I run some Python script in NodeJS to validate the data and then create a temporary file of this JSON data and return it to user as a HTTP GET response.
Right now I'm using a timer to delete this temporary file after 10 seconds, which is bad. I want to know how to detect when the user has fully downloaded the file to their local disk from the browser so I can delete this temporary file in backend.
The client Angular code:
$scope.downloadForm = function() {
var data = formDataFactory.getDataForSubmission();
var url = '/FormSubmission/DownloadData';
// Below POST call will invoke NodeJS to write the temporary file
$http.post(url, data)
.success(function(data, status) {
$scope.downloadPath = data.filePath;
$scope.downloadFile = data.fileName;
url = '/tmp/forms/' + $scope.downloadFile;
// If the temporary file writing is successful, then I get it with a GET method
$http.get(url)
.success(function(data, status) {
$log.debug("Successfully got download data");
$window.location = $scope.downloadPath;
})
.error(function(data, status) {
$log.error("The get data FAILED");
});
})
.error(function(data, status) {
$log.error("The post data FAILED");
});
}
$scope.download = function() {
$scope.downloadForm();
setTimeout(function() { //BAD idea
$scope.deleteForm($scope.downloadPath);
}, 10000);
}
The server NodeJS code:
// POST method for creating temporary JSON file
router.post('/FormSubmission/DownloadData', function(req, res) {
if (!req.body) return res.sendStatus(400); // Failed to get data, return error
var templateString = formTmpPath + 'form-XXXXXX.json';
var tmpName = tmp.tmpNameSync({template: templateString});
fs.writeFile(tmpName, JSON.stringify(req.body, null, 4), function(err) {
if (err) {
res.sendStatus(400);
} else {
res.json({ fileName: path.basename(tmpName), filePath: tmpName, out: ''});
}
});
});
// Get method for downloading the temporary form JSON file
router.get('/tmp/forms/:file', function(req, res) {
var file = req.params.file;
file = formTmpPath + file;
res.download(file, downloadFileName, function(err) {
if (err) debug("Failed to download file");
});
});
Update:
I'm trying to use a stream now to send the data back, but for some reason this get method is called twice!? Can't understand why!!
// Get method for downloading the temporary form JSON file
router.get('/tmp/forms/:file', function(req, res) {
var filename = "ipMetaData.json";
var file = req.params.file;
file = formTmpPath + file;
var mimetype = mime.lookup(file);
const stats = fs.statSync(file);
res.setHeader('Content-disposition', 'attachment; filename=' + filename);
res.setHeader('Content-type', mimetype);
res.setHeader('Content-Length', stats.size);
console.log("Will send the download response for file: ", file);
//var path = __dirname + "\\..\\tmp\\forms\\form-auSD9X.json";
console.log("Creating read stream for path: " + file);
var stream = fs.createReadStream(file);
// This will wait until we know the readable stream is actually valid before piping
stream.on('open', function () {
// This just pipes the read stream to the response object (which goes to the client)
stream.pipe(res);
});
// This catches any errors that happen while creating the readable stream (usually invalid names)
stream.on('error', function(err) {
console.log("Caught an error in stream"); console.log(err);
res.end(err);
});
stream.on('end', () => {
console.log("Finished streaming");
res.end();
//fs.unlink(file);
});
});
if I understand your problem correctly, you can do this in different ways, but easiest way is first, remove the timer to remove the file, and remove it after the download completes from the backend as follows
router.get('/tmp/forms/:file', function(req, res) {
var file = req.params.file;
file = formTmpPath + file;
res.download(file, downloadFileName, function(err) {
if (err) debug("Failed to download file");
else {
// delete the file
fs.unlink(file,function(err){
if(err) debug(err);
})
}
});
});
The problem was with doing a get call and then change location to the file path which has the same path. I changed my API path and used the stream .on('end', callback) to remove the file.
// If the temporary file writing is successful, then I get it with a GET method
$http.get(url) --> this URL should be different from $window.location
.success(function(data, status) {
$log.debug("Successfully got download data");
$window.location = $scope.downloadPath;
})
.err
I have a really quick question:
I have an img tag (in my template file) that holds an image and in my Angular Controller I call:
var image = document.getElementById('srcImage');
I want to send this image ^ to the backend (I am using REST). The url I would use for this POST method is:
'/api/v1/images/addImage'
I've tried ng-file-upload and $http.post, but nothing seems to be working. Is there any way that I can simply send this image over to the server so I can store it in a database or file system? I am open to any solutions to making this happen.
Thanks!!
You can use below libraries, they have good documentation also
For Frontend -
https://github.com/nervgh/angular-file-upload
For Backend -
https://github.com/expressjs/multer
Sample Snippet -
In HTML :
<input type="file" nv-file-select="" uploader="ctrl.uploader" multiple />
Angular Controller :
vm.uploader = new FileUploader({
url: 'http://' + SERVER_URL + '/upload',
formData: [{
id: 1
}]
});
vm.save = function() {
vm.uploader.onBeforeUploadItem = function (item) {
console.log(item);
/* some action */
};
vm.uploader.onSuccessItem = function (item, imgResponse, status, headers) {
console.log(item);
console.log(imgResponse);
console.log(status);
console.log(headers);
/* some action */
};
};
Node Server :
var fs = require('fs');
var multer = require('multer');
var fileName = '';
var storage = multer.diskStorage({
destination: function (req, file, cb) {
var dirPath = 'path/to/save/file'
if (!fs.existsSync(dirPath)) {
var dir = fs.mkdirSync(dirPath);
}
cb(null, dirPath + '/');
},
filename: function (req, file, cb) {
var ext = file.originalname.substring(file.originalname.lastIndexOf("."));
fileName = Date.now() + ext;
cb(null, fileName);
}
});
// Assuming Express -
app.get('/upload', function (req, res) {
var upload = multer({
storage: storage
}).array('file', 12);
upload(req, res, function (err) {
if (err) {
// An error occurred when uploading
res.json(err);
}
res.json(fileName);
});
});
You can try multipart/form-data like this:
<form id = "uploadForm"
enctype = "multipart/form-data"
action = "/api/photo"
method = "post"
>
<input type="file" name="userPhoto" />
<input type="submit" value="Upload Image" name="submit">
</form>
I am now using angular-file-upload packages to upload files. After I press item.upload(), it claims to be successfully uploaded the file, but I see the req.body is empty. Please Help!
Here is the angular code to handle it:
var uploader = $scope.uploader = $fileUploader.create({
scope: $scope, // to automatically update the html. Default: $rootScope
url: '/api/teams/upload',
formData: [
{ key: 'value' }
],
filters: [
function (item) { // first user filter
$scope.previewImage(item);
return true;
}
]
});
And here is the way to trigger the upload:
uploader.bind('afteraddingfile', function (event, item) {
// console.info(item.file);
console.info('After adding a file', item);
// console.log('item.upload();');
item.upload();
});
And finally here is the express js code:
exports.upload = function(req, res) {
// console.log('req.headers');
// console.log(req.headers);
console.log('req.body');
console.log(req.body);
What wrong's with it?
First make sure your POST is encoded as enctype="multipart/form-data"....
In Express 4 you need to set the body parser in your server:
var bodyParser = require('dy-parser');
//...
var app = express();
//...
app.use(bodyParser()); // pull information from html in POST
var busboy = require('connect-busboy');
app.use(busboy());
In earlier version of Express you only needed to add the body parser from the framework itself and files will be store on the configured location:
app.use(express.bodyParser({limit: '10mb', uploadDir: __dirname + '/public/uploads' })); // pull information from html in POST
Since version 4 removed support for connect now you need to add your custom support for multipart/form data to parser multi/part POSTs, so you will have to to do something like:
var fs = require('fs');
var busboy = require('connect-busboy');
//...
app.use(busboy());
//...
app.post('/api/teams/upload', function(req, res) {
var fstream;
req.pipe(req.busboy);
req.busboy.on('file', function (fieldname, file, filename) {
console.log("Uploading: " + filename);
fstream = fs.createWriteStream(__dirname + '/files/' + filename);
file.pipe(fstream);
fstream.on('close', function () {
res.redirect('back');
});
});
});
On the client side you need to call the $upload.upload To start the upload