show folders using node.js and handlebars.js - node.js

i want to show folders that i ahve aunder some directory,
im using express3-handlebars.
html code:
{{gallery}}
here what i have:
res.render('pages/gallery', {
title: 'גלריה',
HomeActive:'',
ContactActive:'',
AboutActive:'',
GalleryActive:'active',
MapActive:'',
EmailActive:'',
helpers:{
gallery:function(){
var albums = [],
albumDir = './public/img/albums';
eventEmitter.on('readFolder',function(){
var albums = [];
fs.readdir(albumDir, function (err, files) {
if (err) console.log(err);
for (var i = 0; i < files.length; i++) {
if (files[i].indexOf('.') == -1)
albums.push({album: files[i]});
}
console.log("read " + files);
eventEmitter.emit('folderRead',files);
});
});
console.log(albums);
eventEmitter.emit('readFolder');
return eventEmitter.on('folderRead',function(files){
console.log("end " + files)
return files;
});
}
},
PagitionHide:'hide'
});
i had a problem to set albums without the eventEmitters, its seems like, doing:
gallery:function(){
var albums = [],
albumDir = './public/img/albums';
fs.readdir(albumDir, function (err, files) {
if (err) console.log(err);
for (var i = 0; i < files.length; i++) {
if (files[i].indexOf('.') == -1)
albums.push({album: files[i]});
}
console.log(albums); //logs the folders names.
});
console.log(albums); //logs an empty array
return albums;
}
cause albums to be empty, i guess beacsue of the async of node.js.
but now when the HTML is render its showing and object like so: [object Object]
what am i doing wrong here?

Did you try this ?
{{#each gallery}}
{{album}}
{{/each}}

Related

FormData FileList is returnig as [Object FileList]

It's a simple FileList of images.
Object.keys(pet).forEach((key) => {
if (key === "images") {
formData.append("images", pet[key]); //my fileList
} else {
formData.append(key, pet[key]);
}
});
Console.log of this FileList >>
But when I try to access the same fileList at the back-end, it shows an empty array...
const images = req.files;
console.log(images); // equals to []
And when I tried like this:
const images = req.body.images;
console.log(images); // Returns this: [object FileList].
When I use this same endpoint with Postman works perfectly...
Just found out what was going on... I was passing an Object Array that contains a FileList that is anothar Array so it was never going to work...
Object.keys(pet).forEach((key) => {
if (key === "images") {
let images = Array.from(pet.images[0]); //needed to access the first array first
for (let i = 0; i < images.length; i++) {
formData.append("images", images[i]);
}
} else {
formData.append(key, pet[key]);
}
});

Problems when I upload images and show them in angular and node using the GraphicsMagick

I use this code to upload images in Node:
req.file('image[]')
.upload({
maxBytes: 5000000, // Files limit(in bytes)
dirname: path.resolve(sails.config.appPath, 'assets/images/user') // Path to copy the files
}, function whenDone(err, uploadedFiles) {
if (err) {
// here must delete the created user before the error
console.log(err);
}
var image_real_name = '';
var json = [];
for(var i = 0; i < uploadedFiles.length; i++){
image_real_name = 'images/user/'+path.basename(uploadedFiles[i].fd);
json.push(image_real_name);
}
res(json);
});
but I needed to compress the images to gain space on the server so I used the gm GraphicsMagick:
`
var receiver = new Writable({objectMode: true});
receiver._write = function(file, enc, cb) {
// The output stream to pipe to
var output = require('fs').createWriteStream('assets/images/user/' + file.fd);
gm(file).resize('200', '200').stream().pipe(output);
cb();
};
req.file('image[]').upload(receiver, function(err, files){
if (err) {
// here must delete the created user before the error
console.log(err);
}
var image_real_name = '';
var json = [];
for(var i = 0; i < files.length; i++){
image_real_name = 'images/user/'+path.basename(files[i].fd);
json.push(image_real_name);
}
res(json);
});
`
I am using angular and when I change the direction of the image by binding the new image should be shown but it is not loaded, with the first code all perfect but using gm does not work anymore. I have to restart the page so that the image is displayed.
track:
apparently the image has not been fully processed, if a setTime is entered, the image is displayed correctly:
setTimeout((dataService: DataService, image: any) => {
dataService.getUser().image = image;
}, 1000, this.data, dataI.json().url);
any idea?
There is a finish event when the file is fully loaded, I solved the problem by adding the following:
output.on("finish", function() { return cb(); });

How to access value inside promise?

I'm trying to learn to use promise.
In this case i want to make a web scraper . I'm using request-promise inside this code.
Actually the scraper is working fine. I have missunderstand how to use the return value inside then(). I am also have googled it but no luck and still get stuck. Here is my code.
let categories = scraper.getCategories(promoUrl);
categories.then(function (val) {
let promoPerCategory = [];
for (let i = 0, len = val.length; i < len; i++) {
let json = {
category: val[i].category,
url: val[i].url,
promo: []
}
scraper.getPromoPerCategories(val[i].url)
.then(function (allPromo) {
for (let j = 0, lenResult = allPromo.length; j < lenResult; j++) {
json.promo.push({
imageUrl: allPromo[j].imageUrl,
merchantName: allPromo[j].merchantName,
promoTitle: allPromo[j].promoTitle,
validUntil: allPromo[j].validUntil,
promoUrl: allPromo[j].promoUrl
});
console.log(json.promo[j]);
}
})
promoPerCategory.push(json);
}
result.send({ "promoPerCategory": promoPerCategory });
})
the then() inside function getPromoPerCategories is working fine, i print out the value using console.log(json.promo[j])
But the problem is i want to put array from json.promo[j] into array promoPerCategory so that i can return it using result.send({ "promoPerCategory": promoPerCategory })
Can you please help me to do the right way? any help will be very appreciated.
Result is now like this
{"promoPerCategory": [{"category":"Fashion","url":"https://","promo":[]}
object promo is empty array
Your first problem is that result.send is not waiting for the getPromoPerCategories result. To make that happen, result.send needs to be in a callback of the getPromoPerCategories promise. It also looks like you want to wait for all of your results to come back before calling result.send, so you should use Promise.all and wait on all of the getPromoPerCategories promises. For example:
categories.then(function (val) {
let promoPerCategory = [],
promoPerCategoryPromises = [];
for (let i = 0, len = val.length; i < len; i++) {
let json = {
category: val[i].category,
url: val[i].url,
promo: []
}
var promise = scrapper.getPromoPerCategories(val[i].url)
.then(function (allPromo) {
for (let j = 0, lenResult = allPromo.length; j < lenResult; j++) {
json.promo.push({
imageUrl: allPromo[j].imageUrl,
merchantName: allPromo[j].merchantName,
promoTitle: allPromo[j].promoTitle,
validUntil: allPromo[j].validUntil,
promoUrl: allPromo[j].promoUrl
});
console.log(json.promo[j]);
}
promoPerCategory.push(json);
});
promoPerCategoryPromises.push(promise);
}
Promise.all(promoPerCategoryPromises).then(function() {
result.send({ "promoPerCategory": promoPerCategory });
});
})
var data = [];
scrapper.getPromoPerCategories(val[i].url)
.then(function() {
data.push({'foo': 'bar'});
});
result.send(data);
Or you can just result.send() inside the promise chain.
.then(function() {
var data = [];
data.push({'foo': 'bar'});
result.send(data);
});

Read/write binary data to MongoDB in Node.js

I've been able to successfully write binary data (an image) to MongoDB in Node.js. However I can't find clear documentation on how to read it back.
Here's how I'm writing the image to MongoDB:
var imageFile = req.files.myFile;
var imageData = fs.readFileSync(imageFile.path);
var imageBson = {};
imageBson.image = new db.bson_serializer.Binary(imageData);
imageBson.imageType = imageFile.type;
db.collection('images').insert(imageBson, {safe: true},function(err, data) {
I'd appreciate any pointers on reading the image from Mongo using Node. I'm assuming there's a function like "db.bson_deserializer...". Thanks!
Found the answer:
var imageFile = req.files.myFile;
fs.exists(imageFile.path, function(exists) {
if(exists)
{
console.log("File uploaded: " + util.inspect(imageFile));
fs.readFile(imageFile.path, function(err, imageData) {
if (err) {
res.end("Error reading your file on the server!");
}else{
//when saving an object with an image's byte array
var imageBson = {};
//var imageData = fs.readFileSync(imageFile.path);
imageBson.image = new req.mongo.Binary(imageData);
imageBson.imageType = imageFile.mimetype;
console.log("imageBson: " + util.inspect(imageBson));
req.imagesCollection.insert(imageBson, {safe: true},function(err, bsonData) {
if (err) {
res.end({ msg:'Error saving your file to the database!' });
}else{
fs.unlink(imageFile.path); // Deletes the file from the local disk
var imageBson = bsonData[0];
var imageId = imageBson._id;
res.redirect('images/' + imageId);
}
});
}
});
} else {
res.end("Oddly your file was uploaded but doesn't seem to exist!\n" + util.inspect(imageFile));
}
});
The MongoDB part isn't complicated. Once a Buffer is in the model, just let the db save it. MongoDB converts that into BinData. 80% of this code is just getting an image into and out of a PNG file.
People say don't store images in MongoDB, but icons/thumbs are tiny. Having said that, it might be a good idea to have an icons collection and only store them once using a hash of the image data as the _id.
model class example
class MyModel {
_icon: Buffer
get icon(): Buffer {
return this._icon
}
set icon(value: Buffer) {
this._icon = value
}
}
image helper
static async loadImage(url: string) {
var files = require('../lib/files')
var buffer = await files.urlContents(url, true)
return buffer
}
static async saveImage(image: Buffer, path: string) {
var files = require('../lib/files')
files.write(path, image.buffer)
return path
}
files helper
function urlResponse(url, binary) {
var request = require("request")
if (binary)
request = request.defaults({ encoding: null })
return new Promise(function (resolve, reject) {
request(url, function (error, res, body) {
if (error || res.statusCode !== 200 || body.includes('Incomplete response received from application'))
resolve({ statusCode: res?.statusCode !== 200 ? (res?.statusCode || 500) : 500 });
else
resolve(res);
});
});
}
async function urlContents(url, binary) {
var res = await urlResponse(url, binary)
if (binary)
return Buffer.from(res.body)
else
return res.body
}
function write(fileName, contents) {
fs.writeFileSync(fileName, contents)
}
mongodb helper
// ...saving
myModel.icon = loadImage('http://some.site.com/image.png')
collection.insertOne(myModel)
// ..getting
myModel = collection.findOne(query) // now myModel contains icon
saveImage(myModel.icon, '/home/toddmo/pictures/wow.png')

For-loop and async callback in node.js?

I'm new to JavaScript and to node.js. I want to loop through a directory and add all file stat (not other directories) to an array. As you see below there is a problem with my code since the callback will probably get called after the for loop has finished so using the "i"-variable in the callback method will not work. But how should the code look so that the below snippet works? Does it have something to do with closures?
Thanks for help!
fs.readdir(SYNCDIR, function(err1, files) {
var filesOnly = [];
if(!err1) {
for(var i = 0; i < files.length; i++) {
var imgFilePath = SYNCDIR + '/' + files[i];
fs.stat(imgFilePath, function(stat){
if (stat.isFile()){
filesOnly[i] = stat; // This will not be correct since the for-loop has finished
}
});
}
}
});
You are right about needing to use a closure. You should wrap the contents of the for loop in a self-invoking function to preserve the value of i for each iteration.
fs.readdir(SYNCDIR, function(err1, files) {
var filesOnly = [];
if(!err1) {
for(var i = 0; i < files.length; i++) {
(function(i) {
var imgFilePath = SYNCDIR + '/' + files[i];
fs.stat(imgFilePath, function(stat){
if (stat.isFile()){
filesOnly[i] = stat;
}
});
})(i);
}
}
});
One way is to rewrite the innards of the loop to use a closure:
fs.readdir(SYNCDIR, function(err1, files) {
var filesOnly = [];
if(!err1) {
for(var i = 0; i < files.length; i++) {
(function(index) {
var imgFilePath = SYNCDIR + '/' + files[index];
fs.stat(imgFilePath, function(stat){
if (stat.isFile()){
filesOnly[index] = stat;
}
});
})(i);
}
}
});
A better looking example, achieving the same, using Array.prototype.forEach:
fs.readdir(SYNCDIR, function(err1, files) {
var filesOnly = [];
if(!err1) {
files.forEach(function(file, i) {
var imgFilePath = SYNCDIR + '/' + file;
fs.stat(imgFilePath, function(stat){
if (stat.isFile()){
filesOnly[i] = stat;
}
});
});
}
});
Alternatively use the new threads module ( https://github.com/robtweed/Q-Oper8 ) and then you can do all this stuff much more simply using standard synchronous coding within the threads child processes, since they only deal with one user's request at a time.
Goodbye to async logic and nested callbacks!

Resources