Build Image Gallery with LightBox using Angular and Bootstrap – Harshad Chopra

Build Image Gallery with LightBox using Angular and Bootstrap – Harshad Chopra

Hello Friends Today in this tutorial we will be building a Advanced Image Gallery with LightBox Effect using Angular and Bootstrap Technologies. All the Source Code of the Application is given below. And also the screenshots of the application is given below. You can also extend the application according to your needs.

Features of this Project

  • Quite easy to use, only one configure you just need, no complicated config stuff
  • Folder explore which is similar that you use explorer or finder in you pc
  • Image gallery with preview mode and two size the image thumbnail
  • Provide folder compress and download

Dependencies

Usage

  • npm install
  • vi ./config/default.json config the “homePath”
  • npm start
  • Visit http://localhost:8000/
  • Have Fun and Just Roll With It!

Source Code

Create a new Directory and name it whatever you want and inside it create a new package.json file and copy paste the below code and run the command npm install this will install all the dependencies.

package.json

{
  "name": "folder-gallery",
  "version": "0.0.1",
  "scripts": {
    "start": "node ./bin/www",
    "postinstall": "bower install"
  },
  "description": "folder explore and image gallery viewer",
  "dependencies": {
    "config": "^1.17.1",
    "debug": "~2.1.1",
    "ejs": "^2.3.4",
    "express": "~4.12.2",
    "express-authentication": "^0.3.2",
    "express-authentication-basic": "^0.3.1",
    "express-session": "^1.13.0",
    "jade": "~1.9.2",
    "lodash": "^3.10.1",
    "lwip": "0.0.8",
    "serve-favicon": "~2.2.0",
    "serve-index": "hcnode/serve-index",
    "tar-fs": "^1.8.1"
  }
}

Now create another bower.json file in the same directory and copy paste the below code.

bower.json

{
  "name": "folder-gallery",
  "description": "folder explore and image gallery viewer",
  "main": "",
  "authors": [
    "hcnode <7372@163.com>"
  ],
  "license": "MIT",
  "moduleType": [],
  "homepage": "",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "bootstrap": "~3.3.6",
    "font-awesome": "~4.4.0",
    "angular": "~1.4.0",
    "ngGallery": "git://github.com/hcnode/ngGallery"
  }
}

Make a new Directory called config and inside the directory make a new file called default.json and copy paste the following code

default.json

{
  "homePath" : "/Users/harry/photo/",  
  "thumbSize": 500 
}

Change the homePath accordingly to your Directory Strucutre

Now we will make Views for the Application. Make a new Directory called views and inside it we will make three files

  • error.jade   => This is the layout file which will be responsible for displaying errors
  • gallery.ejs  => This is the layout file which will be responsible for displaying gallery images
  • layout.jade  => This is the layout file which will be containing the layout for displaying images.

error.jade

extends layout

block content
  h1= message
  h2= error.status
  pre #{error.stack}

gallery.ejs

<!DOCTYPE html>
<html ng-app="app">
<head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/bower_components/ngGallery/src/css/screen.css">
    <link rel="stylesheet" href="/bower_components/ngGallery/src/css/ngGallery.css">
    <link rel="stylesheet" href="/bower_components/font-awesome/css/font-awesome.min.css">

    <script src="/bower_components/jquery/dist/jquery.min.js"></script>
    <script src="/bower_components/angular/angular.min.js"></script>

    <script src="/bower_components/ngGallery/src/js/ngGallery.js"></script>

</head>
<body ng-app="app" style="padding: 20px">
<div ng-controller="CtrlGallery as ctrl">
    <ng-gallery images="ctrl.images" editShow="false"></ng-gallery>
</div>

<script>
    
    function createImages(path, files){
        var images = [];
        for(var i=0;i<files.length;i++) {
            images.push({ // ngGallery所需要的数据对象
                thumb: path + "/" +  files[i] + "/_thumb",
                img: path + "/"+ files[i] + "/_image",
                description: (/\/([^\/]+)$/.test(path) && RegExp.$1)
            });
        }
        return images;
    }
    var app = angular.module("app", ['jkuri.gallery']).controller('CtrlGallery', function($scope, $timeout) {
        var files = <%- JSON.stringify(files) %>; // 照片的json数组:["xxx.jpg", "yyy.jpg"]
        var path = "<%- path %>";
        var images = createImages(path, files);
        var that = this;
        // 每次加载三张图片,按顺序加载
        that.images = images.splice(0, 3);
        window.ngGalleryImageOnload = function (image) {
            if(images.length > 0) {
                $timeout(function () {
                    that.images.push(images.shift());
                })
            }
        };
    });
</script>
</body>
</html>

layout.jade

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
  body
    block content

After this we need to make some css to style the application so make a new directory called as public/stylesheets and make a new file called style.css and copy paste below code.

style.css

body {
  padding: 50px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
  color: #00B7FF;
}

Now make a new directory called as bin and inside it directory make a new file called as www this will be the server of the application so just copy paste the below code.

www.js

var app = require('../app');
var debug = require('debug')('folder-explore:server');
var http = require('http');

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.PORT || '8000');
app.set('port', port);

/**
 * Create HTTP server.
 */

var server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}

Lastly we will hook all the things up by creating app.js in the project directory and copy paste the below code.

var express = require('express');
var path = require('path');
var fs = require('fs');
var serveIndex = require('serve-index');
var tar = require('tar-fs');
var _ = require('lodash');
var lwip = require('lwip');
var config = require('config');
var session = require('express-session')
var basic = require('express-authentication-basic');

var app = express();

// obtain config from /config/default.json
var homePath = config.get("homePath"); // home path
var thumbSize = config.get("thumbSize");// thumb size


app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }}));

app.use(basic(function(challenge, callback) {
	if (challenge.username === 'xxx' && challenge.password === 'xxx') {
		callback(null, true, { user: 'admin' });
	} else {
		callback(null, false, { error: 'INVALID_PASSWORD' });
	}
}));
app.use(function (req, res, next) {
	if (req.authenticated || req.session.isAuth) {
		req.session.isAuth = true;
		next();
	} else {
		res.set("WWW-Authenticate", "Basic realm=\"client Login\"");
		res.statusCode =401;
		res.end();
	}
});
// bower dependence router
app.use('/bower_components', express.static(__dirname + '/bower_components'));

// dir compress router
app.get(/.*\/_tar$/, function (req, res, next) {
	var baseUrl = req.originalUrl;
	var dir = homePath + getPath(baseUrl, "_tar");
	var tarName = "tar";
	if(dir){
		tarName = (/\/?([^\/]+)\/?$/.test(dir) && RegExp.$1) || tarName;
	}
	if(fs.existsSync(dir)) {
		res.set("Content-Disposition", 'attachment;filename="' + encodeURIComponent(tarName) + '.tar"');
		tar.pack(dir).pipe(res);
	}else{
		next();
	}
});


// thumb image generater router
app.get(/.*\/_thumb$/, function (req, res, next) {
	var baseUrl = req.originalUrl;
	var file = getPath(baseUrl, "_thumb");
	var fullFile = homePath + file;
	var thumbName = fullFile.replace(/\//gi, "_");
	if(fs.existsSync(fullFile)) {
		var thumbFile = __dirname + "/thumb/" + thumbName;
		if(!fs.existsSync(__dirname + "/thumb/")){
			fs.mkdirSync(__dirname + "/thumb/");
		}
		if(fs.existsSync(thumbFile)){
			res.sendFile(thumbFile);
		}else {
			lwip.open(fullFile, function (err, image) {
				var max = Math.max(image.width(), image.height());
				if(max < thumbSize){
					res.sendFile(fullFile);
				}else {
					var ratio = 1;
					if(image.width() > image.height()){
						ratio = thumbSize / image.width();
					}else{
						ratio = thumbSize / image.height();
					}
					image.scale(ratio, function (err, image) {
						image.writeFile(thumbFile, function (err) {
							res.sendFile(thumbFile);
						});
					});
				}
			})
		}
	}else{
		next();
	}
});

// gallery page router
app.get(/.*\/_gallery$/, function (req, res, next) {
	var baseUrl = req.originalUrl;
	var path = getPath(baseUrl, "_gallery");
	var fullPath = homePath + path;
	if(fs.existsSync(fullPath)){
		var images = [];
		fs.readdir(fullPath, function (err, files) {
			_.forEach(files, function (file) {
				if(file.indexOf(".") != 0) {
					var stat = fs.statSync(fullPath + '/' + file);
					if (!stat.isDirectory()) {
						if (/\.(PNG)|(JPG)$/ig.test(file)) {
							images.push(file);
						}
					}
				}
			});
			res.render(__dirname + "/views/gallery.ejs", {files : images, path : path});
		});
	}else{
		next();
	}
});

// image router
app.get(/.*\/_image$/, function (req, res, next) {
	var baseUrl = req.originalUrl;
	var file = getPath(baseUrl, "_image");
	var fullFile = homePath + file;
	if(fs.existsSync(fullFile)) {
		res.sendFile(fullFile);
	}else{
		next();
	}
});

function getPath(baseUrl, action) {
	baseUrl = decodeURIComponent(baseUrl);
	var path = baseUrl.substring(0, baseUrl.length - action.length - 1);
	//console.log(baseUrl);
	return path.replace(/\/\//gi, "\/");
}

if(fs.existsSync(homePath)) {
	app.use('/', serveIndex(homePath, {'icons': true, view: "details"}));
	app.use('/', express.static(homePath));
}

// catch 404 and forward to error handler
app.use(function (req, res, next) {
	var err = new Error('Not Found');
	err.status = 404;
	next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
	app.use(function (err, req, res, next) {
		res.status(err.status || 500);
		res.render('error', {
			message: err.message,
			error: err
		});
	});
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
	res.status(err.status || 500);
	res.render('error', {
		message: err.message,
		error: {}
	});
});


module.exports = app;

Leave a Reply

Close Menu