zip and download folders to local using nodejs - javascript

How to zip and download folder from D:/downloads path. As a 1st step, I was able to create a folder inside 'downloads' with dummy content. As a next step I want to zip and download that folder.
async downloadFolder(selectedProduct) {
try {
let completeZip = await this.jobService.zipBlobs(selectedProduct.path, this.role).toPromise();
if(completeZip['status']=='success'){
let download = await this.jobService.downloadBlobs(selectedProduct.path, this.role).toPromise();
console.log(download)
}
} catch (error) {
console.log(error);
}
}
API:
Once file is written , I want to zip that folder and download that folder to local but nothing happens
exports.zipBlobs = async function (req, res) {
var userrole = req.body.userrole;
var path = req.body.path;
fileUploadPath="d:/downloads";
blobService.listBlobsSegmentedWithPrefix(containerName, path, null, (err, data) => {
if (err) {
reject(err);
} else {
data.entries.forEach(entry => {
console.log(entry.name);//'155ce0e4-d763-4153-909a-407dc4e328d0/63690689-e183-46ae-abbe-bb4ba5507f1a_MULTI_0_3/output/res2/res2.fcs';
if (fs.existsSync(fileUploadPath)) {
var sourceFilePath = fileUploadPath +'/'+entry.name ;
if (!fs.existsSync(sourceFilePath)) {
fs.mkdir(require('path').dirname(sourceFilePath), { recursive: true }, (err) => {
if (err) {
console.log("Failed :" + err);
}
else{
console.log('folder created,create file');
const fstream = fs.createWriteStream(sourceFilePath);
fstream.write('fileContent');
fstream.end();
fstream.on("finish", f => {
console.log('finish',f) ;
});
fstream.on("error", e => {
console.log('error',e);
});
}
});
}else{
console.log('folders already exists,create file');
const fstream = fs.createWriteStream(sourceFilePath);
fstream.write('fileContent');
fstream.end();
fstream.on("finish", f => {
console.log('finish',f) ;
});
fstream.on("error", e => {
console.log('error',e);
});
}
}else{
console.log('downloads folder does not exists!')
}
});
}
});
}
API to zip and download folder :
exports.downloadFolders = async function (req, res) {
var userrole = req.body.userrole;
var path = req.body.path;
try {
const folderpath = 'D:\downloads\622b6a148a813f18b8b2de81';
require('child_process').execSync(`zip -r archive *`, {
cwd: folderpath
});
// does not create zip, neither downloads
res.download(folderpath + '/archive.zip');
return;
}catch(error_1) {
res.status(200).json({
status: error_1
});
return;
}
}

In Javascript strings, backslashes must be doubled:
const folderpath = 'D:\\downloads\\622b6a148a813f18b8b2de81';
Without doubling them, you effectively get
const folderpath = 'D:downloads22b6a148a813f18b8b2de81'
because '\d' === 'd' and '\6' is a non-printable character.
You can also write the result of zip to the standard output and pipe it into the response object:
res.set("Content-Disposition", "attachment;filename=archive.zip");
require("child_process").exec("zip -r - *", {
cwd: folderpath
}).stdout.pipe(res);

This is something I used in one of my projects where I needed the whole directory downloaded as zip:
require the following library:
const zipdir = require('zip-dir')
Then, when you need to download the zip, call it as follows:
zipdir(
'D:/downloads/622b6a148a813f18b8b2de81',
{ saveTo: 'D:/downloads/622b6a148a813f18b8b2de81/archive.zip' },
(err, buffer) => {
if (err) throw err;
console.log('New zip file created!');
}
);
Following is the API signature:
app.get('/api/zip', function (req, res) {
//create new zip
zipdir(
'D:/downloads/622b6a148a813f18b8b2de81',
{ saveTo: 'D:/downloads/622b6a148a813f18b8b2de81/archive.zip' },
(err, buffer) => {
if (err) throw err;
console.log('New zip file created!');
res.download('D:/downloads/622b6a148a813f18b8b2de81/archive.zip');
}
);
});

Related

download csv to browser downloads node.js

I have this code:
fs = require("fs");
var downloadData = "select * from PRODUCTS"
ibmdb.open(req.session.ibmdbconnDash, function (err, conn) {
if (err) return console.log(err);
conn.query(downloadData, function (err, rows) {
if (err) {
console.log(err);
}
const ws = fs.createWriteStream("productsDownloaded.csv");
const jsonData = JSON.parse(JSON.stringify(rows));
console.log("jsonData", jsonData);
fastcsv
.write(jsonData, { headers: true })
.on("finish", function() {
console.log("Write to productsDownloaded.csv successfully!");
})
.pipe(ws);
var value = true;
res.render("edit-products", {
page_title: "edit-products",
data: rows,
userName: req.session.username,
FN: req.session.firstname,
LN: req.session.lastname,
CO: req.session.company,
value: value,
});
conn.close(function () {
console.log("closing function p1 of delete product");
});
});
});
however, it downloads but it doesn't go anywhere. So when im testing locally it goes into my vs code directory, but i want it to download by the browser, so do i have to send something to the front end? Like if i press the download button and trigger this function, it should download to the users directory. how can i achieve this?

Node File upload issue

I am constantly getting the printed out message of "No File Upload" Failed when I select my image and hit upload. It never goes to true..
As you can see, I am not actually uploading here. Just testing the req.files is there something wrong in my router.post? Any input would be appreciated.
router.post('/upload', async (req, res) => {
try {
if(!req.files) {
res.send({
status: false,
message: 'No file uploaded'
});
} else {
res.send({
status: true,
message: 'Files are uploaded',
data: data
});
}
} catch (err) {
res.status(500).send(err);
}
})
module.exports=router
can you share your controller file of it and also you are using async function but not defining await inside of that function,must have to use 'await' when executing async function.
here is the controller file AAmir
let fs = require('fs');
let async = require('async');
function uploaddownFiles(connection, fromFolder, toFolder, sftpmethod) {
return new Promise((resolve, reject) => {
// Getting all file list in given folder of local machine
let fileList = fs.readdirSync(fromFolder);
// filter only files not folders
fileList = fileList.filter(file => {
if (file.includes('.')) return true;
return false;
});
console.log('Total files: ', fileList.length)
if (!fileList.length) return reject('No file to send')
connection.sftp(function (err, sftp) {
if (err) return;
async.eachOfSeries(fileList, (file, key, cb) => {
let moveFrom = `${fromFolder}/${file}`;
let moveTo = `${toFolder}/${file}`;
if (sftpmethod=== 'put')
sftp.fastPut(moveFrom, moveTo, {}, function (uploadError) {
if (uploadError) return cb(uploadError);
console.log("Successfully Uploaded", file);
cb();
});
else if (sftpmethod === 'get')
sftp.fastGet(moveFrom, moveTo, {}, function (uploadError) {
if (uploadError) return cb(uploadError);
console.log("Successfully Downloaded", file);
cb();
});
}, function (err) {
if (err) {
console.log(err);
return reject(err);
} else {
console.log('all files have been uploaded/downloaded');
return resolve();
}
})
});
});
}

Upload images to MongoDB after compressing and delete images from the Multer uploads directory

I am very new to development related work. PLEASE HELP
I am trying to upload 10 images taken from user (implemented via MULTER) to the mongoDB database but, before final upload I want to compress the images using SHARP.
I tried doing this using callbacks. But FAILED.
This is what I want to do:
use MULTER to upload an array of 10 images to the UPLOADS/IMAGES directory.
FOR EACH file in the array use SHARP to compress the file and store the new compressed .jpeg file to UPLOADS/COMPRESSED directory.
THEN
use fsPromises.readFile to read the newly compressed image in the UPLOADS/COMPRESSED directory.
THEN
make an object : const toInsertImgData = { data: result, contentType: "image/jpeg"};
and push it in an array called imgArray. Here result is binary data read in the previous step.
THEN
use fsPromises.unlink to remove all files in UPLOADS/IMAGES and UPLOADS/COMPRESSED
THEN
Use the imgArray to make a document to save in the posts collection of the Database.
Right now every time the imgArray is empty when I want to use it at the end. I know that PROMISES or AYSNC/AWAIT can help. But I am not sure how to implement it.
Please help .
THANKYOU IF YOU HAVE READ THIS FAR
Here is my code:
const promises = [];
app.post("/compose/:id", upload.array("image", 10), (req, res) => {
const id = req.params.id;
const imgArray = [];
const caption = req.body.caption;
const now = new Date();
req.files.forEach((file) => {
const compressedImgPath =__dirname +"/public/uploads/compressed/" +now.getDate() +"-" +(now.getMonth() + 1) +"-" +now.getFullYear() +"-" +now.getTime() +".jpeg";
sharp(file.path)
.resize(640, 480)
.jpeg({
quality: 80,
chromaSubsampling: "4:4:4",
})
.toFile(compressedImgPath)
.then(() => {
fsPromises.readFile(compressedImgPath)
.then((result) => {
const toInsertImgData = {
data: result,
contentType: "image/jpeg",
};
imgArray.push(toInsertImgData);
})
.then(() => {
promises.push(fsPromises.unlink(compressedImgPath));
promises.push(fsPromises.unlink(file.path));
})
.catch((err) => {
console.log(err);
});
});
});
Promise.all(promises)
.then(() => {
User.findById(id, (err, result) => {
if (!err) {
if (imgArray.length > 0) {
console.log("found user:" + id);
const newPost = new Post({
uId: id,
userName: result.name,
timeStamp: "5th August, 2020 at 2:10PM",
caption: caption,
img: imgArray,
});
newPost.save((err) => {
if (!err) {
console.log("post saved in DB");
res.redirect("/users/" + id.toString());
} else {
console.log(err);
}
});
} else {
console.log("array is empty");
}
}
});
})
.catch((err) => {
console.log(err);
});
});
inside for each , you use async call, which means all .then() are guaranteed to be executed before .forEach end,so promises array can be ambiguous.
One easy fix is to use fs.promises inside.then() and not push it to promises.
Below is a very similar use case that might help solve the mentioned question. I have provided the full API route code to understand it better.
var fs = require('fs');
var path = require('path');
const sharp = require('sharp');
const multer = require('multer');
var storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './public/uploads')
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now())
}
});
var upload = multer({ storage: storage });
app.post('/imageupload', upload.single('image'), async (req, res, next) => {
console.log("Image post request by " + req.user.username);
await sharp('./public/uploads/' + req.file.filename).resize(150, 150)
.rotate()
.png({ quality: 100 }).toFile('./public/uploads/' + req.file.filename + '-thumb');
Member.findOneAndUpdate({ sabhe_id: req.body.sabhe_id },
{
img: {
data: fs.readFileSync(path.join('./public/uploads/' + req.file.filename + '-thumb')),
contentType: 'image/png'
}
}
, function (err) {
if (err) {
console.log(err.message);
}
else {
console.log("Image saved to database " + req.user.username);
fs.unlink('./public/uploads/' + req.file.filename, (err) => {
if (err) {
console.error(err.message);
return
}
});
fs.unlink('./public/uploads/' + req.file.filename + '-thumb', (err) => {
if (err) {
console.error(err.message)
return
}
});
if (req.user.isAdmin) {
res.redirect("/admin")
} else {
res.redirect("/profile");
}
}
}
)
});

Fs operations with Yargs

I have to perform some operations with Yargs.For example-
1- Write in a file using fs module and for every write operation need to create a new file,
2-You must take i/p from user as fileName and keep saving fileNames in one array (array part is not done), in one separate text file
3-Next time when user enters the same fileName , if it exists ask again to give new fileName , and then same as Point 1.
I am facing issues with point 2, how to write as an array in text file, and how to call 'Please provide the fileName' again if user keeps on giving existing fileName.
So far I have done this-
const argv = require('yargs').argv;
const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
if (argv._[0] == 'write') {
rl.question('Please provide the filename:=>', (fileName) => {
fs.writeFile('fileNameList.txt', fileName, err => {
if (err) {
console.log('Error occured');
return;
}
fs.writeFile(fileName, 'Hello', err => {
if (err) {
console.log('Error occurred');
return
}
});
});
rl.close();
});
}
else {
console.log('No write operation');
}
so, when user executes it like node index.js write, it will ask the fileName
you need to refactor your code into methods to show intent properly:
Check if file exists
function ifFileExists(filepath) {
try {
fs.accessSync(filepath, fs.constants.F_OK);
return true;
} catch (e) {
return false;
}
}
Ask for user input
function askForUserInput(message) {
rl.question(message, (fileName) => {
if (ifFileExists(fileName)) {
askForUserInput('File already exists, Please provide a new filename:=>');
} else {
writeToFile(fileName);
rl.close();
}
});
}
write to file
function writeToFile(fileName) {
fs.writeFile('fileNameList.txt', fileName, err => {
if (err) {
console.log('Error occured');
return;
}
fs.writeFile(fileName, 'Hello', err => {
if (err) {
console.log('Error occured');
return
}
});
});
}
use it
if (argv._[0] == 'write') {
askForUserInput('Please provide the filename:=>');
}
else {
console.log('No write operation');
}
your logic to write filenames in fileNameList.txt looks correct.
Have a look at this solution and see, to me it looks like since you have file name as entry you can simply write it to the file and when reading from file add to an array
node.js - how to write an array to file
and
node.js: read a text file into an array. (Each line an item in the array.)
const argv = require("yargs").argv;
const fs = require("fs");
const readline = require("readline");
function ifFileExists(fileName) {
return new Promise((resolve, reject) => {
fs.readFile("array.txt", function (err, arrayData) {
if (err) {
if (err.code === "ENOENT") {
handleWhenArrayFileNotFound(reject, resolve);
} else {
reject("file read error");
}
}
if (arrayData) {
handleWhenArrayExists(arrayData, resolve, fileName);
}
});
});
function handleWhenArrayFileNotFound(reject, resolve) {
let content = fileName;
content += "\n";
fs.writeFile("array.txt", content, (error) => {
if (error) {
console.log("Error occured");
reject("file write error");
}
rl.close();
resolve("created");
});
}
function handleWhenArrayExists(arrayData, resolve, fileName) {
if (fileNamePresentInArray(arrayData, fileName)) {
askForNewName("File already exists, Please provide a new filename:=>");
} else {
resolve("create file");
}
}
}
function fileNamePresentInArray(arrayData, fileName) {
var array = arrayData.toString().split("\n");
return array.includes(fileName);
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
function askForNewName(message) {
rl.question(message, (fileName) => {
fs.readFile("array.txt", function (err, arrayData) {
if (err) {
console.log("array.txt not found");
}
if (arrayData) {
if (fileNamePresentInArray(arrayData, fileName)) {
askForNewName(
"File already exists, Please provide a new filename:=>"
);
} else {
writeToFile(fileName);
rl.close();
}
}
});
});
}
function askForUserInput(message) {
rl.question(message, (fileName) => {
ifFileExists(fileName)
.then((res) => {
writeToFile(fileName, res);
})
.catch((err) => {
console.log(err);
});
});
}
function writeToFile(fileName, data) {
if (data !== "created") {
let content = fileName;
content += "\n";
fs.appendFile("array.txt", content, (err) => {
if (err) console.log(err);
});
}
fs.writeFile(fileName, "You are awesome", (err) => {
if (err) {
console.log("Error occured");
}
});
}
if (argv._[0] == "write") {
askForUserInput("Please provide the filename:=>");
} else {
console.log("No write operation");
}

Uploading files using Skipper with Sails.js v0.10 - how to retrieve new file name

I am upgrading to Sails.js version 0.10 and now need to use Skipper to manage my file uploads.
When I upload a file I generate a new name for it using a UUID, and save it in the public/files/ folder (this will change when I've got this all working but it's good for testing right now)
I save the original name, and the uploaded name + path into a Mongo database.
This was all quite straightforward under Sails v0.9.x but using Skipper I can't figure out how to read the new file name and path. (Obviously if I could read the name I could construct the path though so it's really only the name I need)
My Controller looks like this
var uuid = require('node-uuid'),
path = require('path'),
blobAdapter = require('skipper-disk');
module.exports = {
upload: function(req, res) {
var receiver = blobAdapter().receive({
dirname: sails.config.appPath + "/public/files/",
saveAs: function(file) {
var filename = file.filename,
newName = uuid.v4() + path.extname(filename);
return newName;
}
}),
results = [];
req.file('docs').upload(receiver, function (err, files) {
if (err) return res.serverError(err);
async.forEach(files, function(file, next) {
Document.create({
name: file.filename,
size: file.size,
localName: // ***** how do I get the `saveAs()` value from the uploaded file *****,
path: // *** and likewise how do i get the path ******
}).exec(function(err, savedFile){
if (err) {
next(err);
} else {
results.push({
id: savedFile.id,
url: '/files/' + savedFile.localName
});
next();
}
});
}, function(err){
if (err) {
sails.log.error('caught error', err);
return res.serverError({error: err});
} else {
return res.json({ files: results });
}
});
});
},
_config: {}
};
How do I do this?
I've worked this out now and thought I'd share my solution for the benefit of others struggling with similar issues.
The solution was to not use skipper-disk at all but to write my own custom receiver. I've created this as a Sails Service object.
So in file api/services/Uploader.js
// Uploader utilities and helper methods
// designed to be relatively generic.
var fs = require('fs'),
Writable = require('stream').Writable;
exports.documentReceiverStream = function(options) {
var defaults = {
dirname: '/dev/null',
saveAs: function(file){
return file.filename;
},
completed: function(file, done){
done();
}
};
// I don't have access to jQuery here so this is the simplest way I
// could think of to merge the options.
opts = defaults;
if (options.dirname) opts.dirname = options.dirname;
if (options.saveAs) opts.saveAs = options.saveAs;
if (options.completed) opts.completed = options.completed;
var documentReceiver = Writable({objectMode: true});
// This `_write` method is invoked each time a new file is received
// from the Readable stream (Upstream) which is pumping filestreams
// into this receiver. (filename === `file.filename`).
documentReceiver._write = function onFile(file, encoding, done) {
var newFilename = opts.saveAs(file),
fileSavePath = opts.dirname + newFilename,
outputs = fs.createWriteStream(fileSavePath, encoding);
file.pipe(outputs);
// Garbage-collect the bytes that were already written for this file.
// (called when a read or write error occurs)
function gc(err) {
sails.log.debug("Garbage collecting file '" + file.filename + "' located at '" + fileSavePath + "'");
fs.unlink(fileSavePath, function (gcErr) {
if (gcErr) {
return done([err].concat([gcErr]));
} else {
return done(err);
}
});
};
file.on('error', function (err) {
sails.log.error('READ error on file ' + file.filename, '::', err);
});
outputs.on('error', function failedToWriteFile (err) {
sails.log.error('failed to write file', file.filename, 'with encoding', encoding, ': done =', done);
gc(err);
});
outputs.on('finish', function successfullyWroteFile () {
sails.log.debug("file uploaded")
opts.completed({
name: file.filename,
size: file.size,
localName: newFilename,
path: fileSavePath
}, done);
});
};
return documentReceiver;
}
and then my controller just became (in api/controllers/DocumentController.js)
var uuid = require('node-uuid'),
path = require('path');
module.exports = {
upload: function(req, res) {
var results = [],
streamOptions = {
dirname: sails.config.appPath + "/public/files/",
saveAs: function(file) {
var filename = file.filename,
newName = uuid.v4() + path.extname(filename);
return newName;
},
completed: function(fileData, next) {
Document.create(fileData).exec(function(err, savedFile){
if (err) {
next(err);
} else {
results.push({
id: savedFile.id,
url: '/files/' + savedFile.localName
});
next();
}
});
}
};
req.file('docs').upload(Uploader.documentReceiverStream(streamOptions),
function (err, files) {
if (err) return res.serverError(err);
res.json({
message: files.length + ' file(s) uploaded successfully!',
files: results
});
}
);
},
_config: {}
};
I'm sure it can be improved further but this works perfectly for me.
The uploaded file object contains all data you need:
req.file('fileTest').upload({
// You can apply a file upload limit (in bytes)
maxBytes: maxUpload,
adapter: require('skipper-disk')
}, function whenDone(err, uploadedFiles) {
if (err) {
var error = { "status": 500, "error" : err };
res.status(500);
return res.json(error);
} else {
for (var u in uploadedFiles) {
//"fd" contains the actual file path (and name) of your file on disk
fileOnDisk = uploadedFiles[u].fd;
// I suggest you stringify the object to see what it contains and might be useful to you
console.log(JSON.stringify(uploadedFiles[u]));
}
}
});

Categories

Resources