Simple Image Gallery, Part 4

From the previous parts you should have working upload functionality and a simple directory structure. Something akin to this…

images \
       |-- \ gallery1 \
                      |-- \ image_1.jpg
                      |-- \ image_1_200.jpg
                      |-- \ image_1_800.jpg
                      |-- \ image_2.jpg
                      |-- \ image_2_200.jpg
                      |-- \ image_2_800.jpg
                      |-- \ image_3.jpg
                      |-- \ image_4_200.jpg
                      |-- \ image_4_800.jpg
       |-- \ gallery2 \
                      |-- \ image_1.jpg
                      |-- \ image_1_200.jpg
                      |-- \ image_1_800.jpg
                      |-- \ image_2.jpg
                      |-- \ image_2_200.jpg
                      |-- \ image_2_800.jpg
                      |-- \ image_3.jpg
                      |-- \ image_4_200.jpg
                      |-- \ image_4_800.jpg
       |-- \ etc...

So we have two jobs to complete from here for a simple gallery display. First grabbing a list of folders (galleries) and second to pick out the individual images in a given gallery.

First up, a simple htaccess file to redirect everything to our script…

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

Nothing amazing here.

First up, the galleries. Easiest thing from a code point of view would be to stick them all into an array. So let’s do that…

$galleryArray = array();
foreach(glob("images/*") as $filename) {
	if(is_dir($filename)) {
		$galleryArray[] = $filename;
	}
}
sort($galleryArray);

Simple. We’re looping through the images subfolder and picking up anything that’s a directory and adding it to the array. To finish off, we’ll sort the array too.

I don’t mind how you format your gallery. For the sake of simplicity so you can see how this can be done, I’ll be using a default twitter bootstrap configuration. For this simple example you’ll want to find the “nav” unordered list in your index file and replace it with the following…

foreach($galleryArray as $gallery) {
	$gallery = str_ireplace("images/","",$gallery);
	echo '<li><a href="'.$gallery.'">'.$gallery.'</a></li>' . PHP_EOL;
}

Now assuming you included the code for building the $galleryArray above this code, you should now have a simple but effective menu bar. If you want to get fancy, feel free to. I’m only covering the php here, not the css/design/js.

Now assuming someone clicks on one of those menu items, they’re going to want to see some pics rights? We’ll go for the same process here too. First we find out what they’re requesting to see…

$page_request = strip_tags($_SERVER['REQUEST_URI']); //strips any html

//checks for any "../" in the request and strips out. This prevents unwanted directory navigation
while(stripos($page_request, '../') !== false) {
	$page_request = str_ireplace('../', '', $_SERVER['REQUEST_URI']);
}
$page_request = trim($page_request, '/'); //trim any remaining leading or trailing slashes
$page_request = explode("/",$page_request); //explodes the request into an array
$page_request = $page_request[0]; //grabs just the first bit

A bit long winded, but functional. It’s important to try to cover as many ways to break your script as possible. I think I’ve covered the major approaches, but feel free to correct me. Now we know which gallery the user wants, let’s grab the images from the directory.

$imageArray = array();
foreach(glob("images/".$page_request."/*_200.jpg") as $filename) {
	$imageArray[] = str_ireplace("_200.jpg",".jpg",$filename);
}

Same sort of process as above. Nothing fancy, just scanning the directory, grabbing files that match the pattern and putting them into the array. In this case I’m grabbing the “_200.jpg” files as that’s the size I want. Whatever sizes you’re creating with your thumbnail functions and naming scheme, that’ll be what you want.

All that’s left is to find the point in the page where you want the images and then dump them into some html. For example…

foreach($imageArray as $filename) {
	echo '<img src="'.$filename.'" />';
}

Nice, quick, simple. Next post will be another quick short one. It’s not my strong suit by any measure, but I’m going to use the twitter bootstrap framework to pretty things up and make the whole thing look a little better. Please don’t judge me too harshly.

14
May 2012
AUTHOR Barry Parkin
CATEGORY

Uncategorized

COMMENTS No Comments

Simple Image Gallery, Part 3

Yay the good bit. The drag and drop ooo fancy way. Before going any further there’s a few things you need to know. I can’t stress this enough, so pay attention:

The script below should not be openly accessible. Ideally you want to put this behind some authentication in an administration area or something. Because it bypasses the usual file upload limits controlled by php someone could really clog your server up with crap by overloading this script with uploads.

Clear? Good. First thing is first, the HTML…

<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
	<style>
		#uploadArea {border:2px solid #000;padding:1em;margin:1em;text-align:center;}
	</style>
</head>
<body>
<div id="uploadArea">
	<p>Drop files here to upload!</p>
</div>
<div id="uploadList">
</div>
<div id="completeList">
</div>

<script>
	//javascript will go here
</script>
</body>
</html>

Simple enough. Restyle however you want. This is just a sample you can stuff into your scripts however you want. I don’t think this needs any great explanation.

The javascript. I’m using 3 functions in this process. First one is the easiest…

	//stops default behaviour...
	function preventDefaultAndPropagation(evt) {
		evt.preventDefault();
		evt.stopPropagation();
	}

Dragging and dropping files, especially images, into your browser window and trigger various behaviours. This above function is designed to stop default behaviours and events bubbling up the DOM. (stopPropagation and preventDefault)

Second function…

	function loopThroughDroppedFiles (files) {
		//check for File API support & alert where it doesn't work.
		if (typeof files === "undefined") {
			$('#uploadList').html('No support for the File API in this web browser. <a href="https://www.google.com/chrome">Download Chrome</a>');
		} else {
			for (var i=0, l=files.length; i<l; i++) {
				//send each file to the postFile function to upload
				postFile(files[i]);
			}
		}
	}

When files are dropped into our target element, this is the function needed next. First it’ll check for File API support in the browser and offer Google Chrome as an alternative browser if the test fails. I’ve mentioned before, but it’s worth repeating, that I like my failure conditions first in my code. It’s something I’ve recently discovered and decided on. It makes sure that I remember to handle unexpected/unwanted behaviour and makes following code logic easier in my opinion – by that I mean less scrolling around to find the route through your if(){} statements.

When files are dropped into the target element they’re passed as an array. This function loops through that array and passes each file to the “postFile” function (which is next) to actually post the file to the server.

	//function to post the file and provide status updates
	function postFile(file) {
		//create new elements used for the status information
		var div = $('<div></div>'),
			pb = $('<progress></progress>');
		div.append(pb); //apends progress bar to the div

		// HTTP request - supported by Firefox, Chrome and Safari...
		xhr = new XMLHttpRequest();

		//uploads should update the progress bar to see what's happening...
		xhr.upload.addEventListener("progress", function (evt) {
			if (evt.lengthComputable) {
				$('progress').attr({value:evt.loaded,max:evt.total});
			}
		}, false);

		//start the posting to the target script
		xhr.open("post", "upload.php", true);

		//set request headers
		xhr.setRequestHeader("Content-Type", "multipart/form-data");
		xhr.setRequestHeader("X-File-Name", file.fileName);
		xhr.setRequestHeader("X-File-Size", file.fileSize);
		xhr.setRequestHeader("X-File-Type", file.type);

		//provide the user with a little warning about the file processing delay (the php bit)
		div.append('<p>Uploading "'+file.fileName+'". Processing may take a few seconds...</p>');

		//validate the file types & post the file
		if(file.type == 'image/jpeg' || file.type == 'image/gif' || file.type == 'image/png') {
			xhr.send(file);
		} else {
			div.html('Invalid file type ('+file.type+') for file '+file.fileName);
		}

		//display status information like progress bar/s etc...
		$('#uploadList').append(div);

		//once uploads are complete, return status information & clear any progress...
		xhr.onreadystatechange = function () {
			if (xhr.readyState == 4) {
				//perform ajax request to the status page
				$.ajax({
					type: "GET",
					url: "upload.php",
					data: {status: '1'},
					async: false,
					success: function(data) {
						$('#completeList').html(data); //output the status
						$('#uploadList').html(''); //empty the progress info
					}
				});
			}
		};
	}

Bit of a bigger function, so let’s walk through it…

		//create new elements used for the status information
		var div = $('<div></div>'),
			pb = $('<progress></progress>');
		div.append(pb); //apends progress bar to the div

Self explanatory really. To feedback what’s happening to the user we add some html. If there’s multiple files being uploaded this will create multiple progress bars.

		// HTTP request - supported by Firefox, Chrome and Safari...
		xhr = new XMLHttpRequest();

		//uploads should update the progress bar to see what's happening...
		xhr.upload.addEventListener("progress", function (evt) {
			if (evt.lengthComputable) {
				$('progress').attr({value:evt.loaded,max:evt.total});
			}
		}, false);

		//start the posting to the target script
		xhr.open("post", "upload.php", true);

		//set request headers
		xhr.setRequestHeader("Content-Type", "multipart/form-data");
		xhr.setRequestHeader("X-File-Name", file.fileName);
		xhr.setRequestHeader("X-File-Size", file.fileSize);
		xhr.setRequestHeader("X-File-Type", file.type);

		//provide the user with a little warning about the file processing delay (the php bit)
		div.append('<p>Uploading "'+file.fileName+'". Processing may take a few seconds...</p>');

Bit more complex. We start a new http request (note: not using jQuery’s ajax functionality as it doesn’t handle files – unless someone wants to explain why I’m wrong?). We then add an event listener to the progress event. As the data is posted we’re able to monitor how much data has been sent out of the total. This information is fed to the progress html element. The “open” function defines the parameters of the request: in this case we’re using the “post” method, to a target of “upload.php” and if the operation is asynchronous or not.

The “upload.php” target is our php handling script. More on that once we’re through the javascript. We want the operation to be asynchronous in this case because our target php script has some validation work to do and we want that feedback – for this to work we need a readystate.

The request headers are for the server side of things and are simply passing image info over. The last append bit is just some status info for our user. Namely what image is uploading and a little warning that after upload is complete, results will take a second or to (that’s the php script).

		//validate the file types & post the file
		if(file.type == 'image/jpeg' || file.type == 'image/gif' || file.type == 'image/png') {
			xhr.send(file);
		} else {
			div.html('Invalid file type ('+file.type+') for file '+file.fileName);
		}

		//display status information like progress bar/s etc...
		$('#uploadList').append(div);

		//once uploads are complete, return status information & clear any progress...
		xhr.onreadystatechange = function () {
			if (xhr.readyState == 4) {
				//perform ajax request to the status page
				$.ajax({
					type: "GET",
					url: "upload.php",
					data: {status: '1'},
					async: false,
					success: function(data) {
						$('#completeList').html(data); //output the status
						$('#uploadList').html(''); //empty the progress info
					}
				});
			}
		};

We validate the file type next. This is optional, but it’s worth intercepting an attempted upload of an invalid file type before sending the data. We do this again in the php script (trust issues – as in never trust user data) but that only works after uploading. In the event of an invalid file type, we set the upload status html to reflect this. If all is well, the file actually starts to be sent to the target script.

Next up the status html is actually appended to the document so users can see it. The final section is all about the response. If the readystate is equal to 4 (“loaded” as in complete), the file has been sent. We can now put an ajax request in to the “upload.php” script to request the status after image processing (the php bit).

So now the html and javascript is done, lets work on the upload.php script that handles the server side. You’ll hopefully have seen most of this in the previous parts, so I’ll just go over the extra bits…

<?php

//debugging
$log = '============GET=========='.PHP_EOL;
foreach($_GET as $key => $value) {
	$log .= $key . "\t\t" . $value . PHP_EOL;
}
$log .= '============POST==========' . PHP_EOL;
foreach($_POST as $key => $value) {
	$log .= $key . "\t\t" . $value . PHP_EOL;
}
$log .= '============SERVER==========' . PHP_EOL;
foreach($_SERVER as $key => $value) {
	$log .= $key . "\t\t" . $value . PHP_EOL;
}
file_put_contents("log.txt",$log);

//start a session for storing the feedback on file validation
session_start();

//assuming no images are being posted to the script & status is not being requested we can reset the upload progress...
if(!isset($_GET['status']) && !isset($_SERVER['HTTP_X_FILE_NAME'])) {
	unset($_SESSION['uploaderStatus']);
}

//status requested, so echo it
if(isset($_GET['status'])) {
	echo $_SESSION['uploaderStatus'];
}

//image posted via ajax, so validate and handle it.
if(isset($_SERVER['HTTP_X_FILE_NAME'])) {

	//session variable to store results of processing. Sessions are used to support multiple uploads
	$_SESSION['uploaderStatus'] .=  '<div>';

	//grab and filter the file name - shouldn't have any slashes anyway, but no harm in making sure...
	$uploadFileName = stripslashes($_SERVER['HTTP_X_FILE_NAME']);

	//check that there isn't a file already in existence with that name...
	if(is_file($uploadFileName)) {
		$_SESSION['uploaderStatus'] .=  'Error: a file with that name ('.$uploadFileName.') already exists. Upload aborted.';
	} else {
		//save the content of the input stream...
		file_put_contents($uploadFileName,file_get_contents('php://input'));

		//since user data cannot be trusted we check here for the file mime type...
		$finfo = new finfo(FILEINFO_MIME);
		$fileType .= $finfo->file($uploadFileName) . PHP_EOL;

		//the directory you want to store valid uploads
		$uploadDirectory = 'uploads/'; 

		//validate mime type. I'm accepting anything that's an image here. You can be more/less specific
		if(stripos($fileType, 'image/') !== false) {

			//check directory exists first
			if(!is_dir($uploadDirectory)) {
				//directory doesn't exist, create it (recursively for any subdirectories)
				mkdir($uploadDirectory, 0, true);
			}
			//recheck if directory exists - maybe a bit redundant, but functional and catches both doesn't exist and failed to create
			if(!is_dir($uploadDirectory)) {
				$_SESSION['uploaderStatus'] .=  'Error: upload directory does not exist and could not be created.';
				unlink($uploadFileName); //delete posted file
			} else {
				//directory exists, check if it's writ[b]e[/b]able. - stupid php
				if(!is_writable($uploadDirectory)) {
					$_SESSION['uploaderStatus'] .=  'Error: cannot write to upload directory. Please check permissions';
					unlink($uploadFileName); //delete posted file
				} else {
					//check if the file exists already
					if(is_file($uploadDirectory . $uploadFileName)) {
						//file exists - move_uploaded_file overwrites existing files and this shouldn't be desired behaviour here
						$_SESSION['uploaderStatus'] .=  'Error: a file with that name ('.$uploadFileName.') already exists. Upload aborted.';
						unlink($uploadFileName); //delete posted file
					} else {
						//attempt to move file to desired location
						if(!rename($uploadFileName, $uploadDirectory . $uploadFileName)) {
							//file couldn't be moved
							$_SESSION['uploaderStatus'] .=  'Error: could not move uploaded file ('.$uploadFileName.') to uploads directory';
							unlink($uploadFileName); //delete posted file
						} else {
							//file uploaded and moved to the upload directory - yay!
							$_SESSION['uploaderStatus'] .=  'Successfully uploaded "'.$uploadFileName.'".';
						}
					}
				}
			}
		} else {
			//file is not a valid mime type
			$_SESSION['uploaderStatus'] .=  'Error: file ('.$uploadFileName.') is not an image';
			unlink($uploadFileName); //delete posted file
		}
	}	

	$_SESSION['uploaderStatus'] .=  '</div>';
}

That first section is just some debugging. I find it useful to log post/get/server data for jobs like this. I’ve left it in for this for testing, but you should remove it in production. It dumps data to a text file so you can see what was sent to the script.

The important bits are making sure you start a session (duh) as this ensure for multiple file uploads we can store and return all the results.

If a GET variable called “status” is passed, we echo it. This dumps the session variable back to the uploading script thanks to that ajax call.

If a file is posted to it (using $_SERVER['HTTP_X_FILE_NAME']) then we kick it into the same kind of image validation that we did previously, so no great explanations needed. The only things to explain are:

file_put_contents($uploadFileName,file_get_contents('php://input'));

Simply put, this function saves the contents of the php://input wrapper into the $uploadFileName file. You can read more about this here.

		$finfo = new finfo(FILEINFO_MIME);
		$fileType .= $finfo->file($uploadFileName) . PHP_EOL;

finfo is a useful extension (included as default in php5.3+) for determining file info, including mime type.

You can easily insert the php above into the same script serving your html – just remember that after getting the status or handling the posted file you’ll want an exit(); call or you’ll end up with a whole mess on your html page.

Part 4 will be the final part. Now we’ve got our images all uploaded all we’re interested in is the final gallery itself. This wont be anything fancy as front end design isn’t my forté, but it will be functional (my forté) and easy to manage.

19
Apr 2012
AUTHOR Barry Parkin
CATEGORY

PHP General, Projects

COMMENTS 2 Comments

I’m lazy and a security note

In the previous post (simple image gallery, part 2) I made a small error. Thank’s to a colleague (Ingmar) it was pointed out to me that

<form name="imageUpload" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>" enctype="multipart/form-data">

has an XSS vulnerability. You can read more about this XSS vulnerability here.

I’ve gone back to the post and corrected the script to now read

<form name="imageUpload" method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>" enctype="multipart/form-data">

which is secured.

So why am I lazy? Well because it shouldn’t be an exploitable vector. Recent patches to Apache have closed this exploit. I can’t find which patch or the notes (another reason why I’m calling myself lazy for this post).

Just because my config of Apache doesn’t have this hole, doesn’t mean yours doesn’t. Other web servers may or may not be exploitable in this way.

Regardless, you should always assume all data is tainted. It’s a good habit to get in to.

13
Apr 2012
AUTHOR Barry Parkin
CATEGORY

PHP General

COMMENTS No Comments

Simple Image Gallery, Part 2

Now we’re in a situation where we can easily process images to create resized versions: thumbnails and something resembling “web safe” images (as in <500k, not 5mb each).

In the next part we’ll look at a script that works it’s way through a “upload” directory and performs the necessary processing (image work & file movement). So we need a way to drop images into that directory. Three ways spring to mind from easy to complex (and in turn from boring to fun).

Option 1: ftp (or sftp/ftps, whatever). Le sigh. Deeply boring, but let’s be honest – about as quick and easy as it gets. Image validation shouldn’t be an issue here. The reason being if you’re giving someone ftp access, then generally they’re going to be at least a bit technical and, importantly, a trusted user. A trusted user shouldn’t be trying to hack your app, but we’ll be making sure they don’t/can’t anyway.

Option 2: boring old multipart form upload. First, the html:

<form name="imageUpload" method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>" enctype="multipart/form-data">
	<label for="image">Image upload:</label>
	<input type="file" name="image" />
	<input type="submit" value="Upload" />
</form>

Nothing fancy. Pretty this up as you will.

I’m going to skip a step and go straight into building a function to handle the upload. If you like OO & classes, convert this to a method as you wish. I personally like procedural programming for small projects like this. Function first, explanation second this time…

function validateImageUpload() {
	//check file "is_uploaded" and no errors occurred during upload
	if (!is_uploaded_file($_FILES['image']['tmp_name']) || $_FILES['image']['error'] !== 0) {
		//error or file is not uploaded
		echo 'Error: upload failed';
		return false;
	} else {
		//file is uploaded and no error, let's validate it's an image
		// getimagesize functions on images only and returns error codes on failure, so suppress the errors for now
		$imageSizeArray = @getimagesize($_FILES['image']['tmp_name']);
		if(!is_array($imageSizeArray)) {
			//imagesize failed
			echo 'Error: file is not an image';
			return false;
		} else {
			//image is valid
			return true;
		}
	}
}

By returning true/false you can use simple code blocks like…

if(validateImageUpload()) {
	//image is valid
	//do stuff
} else {
	//image is not valid
	//error handling - feedback to user
}

The function as is echos some errors. Feel free to doll them up by wrapping them in styled

tags or something. Or remove them and use the little block above – it’s your app, your call.

So now we know if the image is an image or not. If it is we want to move it to the “uploads” directory for processing…

if(validateImageUpload()) {
	//image is valid
	//grab images original name - feel free to force this to something else if you want
	$filename = $_FILES['image']['name'];
	$uploadDirectory = 'uploads/';

	//check directory exists first
	if(!is_dir($uploadDirectory)) {
		//directory doesn't exist, create it (recursively for any subdirectories) and set 777 permissions
		mkdir($uploadDirectory, 0777, true);
	}
	//recheck if directory exists - maybe a bit redundant, but functional and catches both doesn't exist and failed to create
	if(!is_dir($uploadDirectory)) {
		echo 'Error: upload directory does not exist and could not be created.';
	} else {
		//directory exists, check if it's writ[b]e[/b]able. - stupid php
		if(!is_writable($uploadDirectory)) {
			echo 'Error: cannot write to upload directory. Please check permissions';
		} else {
			//check if the file exists already
			if(is_file($uploadDirectory . $filename)) {
				//file exists - move_uploaded_file overwrites existing files and this shouldn't be desired behaviour here
				echo 'Error: a file with that name already exists. Upload aborted.';
			} else {
				//attempt to move file to desired location
				if(!move_uploaded_file($_FILES['image']['tmp_name'], $uploadDirectory . $filename)) {
					//file couldn't be moved
					echo 'Error: could not move uploaded file to uploads directory';
				} else {
					//file uploaded and moved to the upload directory - yay!
					echo 'Successfully uploaded.';
				}
			}
		}
	}
}

Remember that the “uploads/” directory is relative to the location of the script. So if your uploads directory is within an images directory you’ll need “images/uploads”.

I find code easier to read when failure conditions are dealt with first – means far less scrolling around when debugging. Your opinion may differ :¬)

Now using the functions in the last part, we can process the images with…

foreach(glob("uploads/*.jpg") as $filename) {
	generateThumbNails($filename, 200); //200px wide thumbnails
	generateThumbNails($filename, 800); //800px wide "big", but not original sized images
}

Now you’ve got original, thumbs and resized “web safe” image versions. So let’s move them to our “gallery” folder. We can do some segmentation of what goes where to create a fancy image gallery in the next part. Some of this will look familiar ;¬)

foreach(glob("uploads/*.jpg") as $filename) {
	//check directory exists first
	if(!is_dir("gallery/")) {
		//directory doesn't exist, create it (recursively for any subdirectories) and set 777 permissions
		mkdir("gallery/", 0777, true);
	}
	//recheck if directory exists - maybe a bit redundant, but functional and catches both doesn't exist and failed to create
	if(!is_dir("gallery/")) {
		echo 'Error: upload directory does not exist and could not be created.';
	} else {
		//directory exists, check if it's writ[b]e[/b]able. - stupid php
		if(!is_writable("gallery/")) {
			echo 'Error: cannot write to upload directory. Please check permissions';
		} else {
			//check if the file exists already
			if(is_file("gallery/" . $filename)) {
				//file exists - copy overwrites existing files and this shouldn't be desired behaviour here
				echo 'Error: a file with that name already exists. Move aborted.';
			} else {
				//copy the file to the new location
				if(!copy($filename, "gallery/" . $filename)) {
					//file could not be copied
					echo 'Error: file could not be copied. Move aborted.';
				} else {
					//file copied, remove old one
					echo $filename . ' successfully moved to new directory';
					if(!unlink($filename)) {
						//couldn't delete old upload file
						echo 'Error: couldn't delete ' . $filename . '.';
					}
				}
			}
		}
	}
}

Option 3: html5 file upload. Oooh fancy! Sadly, it’s nice for users, but nothing majorly new on the php front. I’ll cover this in part 3 along with the actual final image gallery itself (woot)

12
Apr 2012
AUTHOR Barry Parkin
CATEGORY

Uncategorized

COMMENTS No Comments

Simple Image Gallery, Part 1

Building a quick and easy image gallery with php. There’s tons of plugins and libraries, classes, etc… to do this kind of thing. Personally, I like to code everything myself where possible. There’s many ways to make an image gallery and lots of fancy frontend stuff you can do, especially with jQuery, but for this purpose I’m only worrying about the php side.

First things first, for this little app I’m using the GD image library. It’s probably already compiled with your php installation. Check the output of…

phpinfo();

…as this will list GD info.

Assuming all is well with GD, we’re ready to start creating thumbnails. For the sake of speed & bandwidth, you’ll want thumbs. If you’re taking high res shots you can be looking at pictures several megabytes in size, so loading a page full of them will make internet kittens cry.

So, some code. Assume below that $filename is the actual file you’re interested in creating a thumbnail for and $desiredThumbNailWidth is width you require for your thumbnails…

//check file is actually an image by making sure getimagesize returns an array
$imageSizeArray = @getimagesize($filename);
if(is_array($imageSizeArray)) {
    // parse path for the extension
    $fileInfo = pathinfo($filename);

    //check the file type
    if(strtolower($fileInfo['extension']) == 'jpg' ) {
        //GD includes several "imagecreatefrom..." functions for several image types, so use the appropriate function for your image type
        $img = imagecreatefromjpeg($filename);
        list($width, $height) = $imageSizeArray;

        // calculate thumbnail size
        $new_width = $desiredThumbNailWidth;
        $new_height = floor( $height * ( $desiredThumbNailWidth / $width ) );

        // create a new temporary image
        $tmp_img = imagecreatetruecolor( $new_width, $new_height );

        // copy and resize old image into new image
        imagecopyresampled( $tmp_img, $img, 0, 0, 0, 0, $new_width, $new_height, $width, $height );

        // save thumbnail into a file
        $thumbFilename = $fileInfo['dirname'] . '/' . $fileInfo['filename'] . '_' . $desiredThumbNailWidth . '.' . $fileInfo['extension'];
        if(imagejpeg( $tmp_img, $thumbFilename, 80 )) {
            echo " creating thumb/display image";
            echo ' - success!';
        } else {
            echo " creating thumb/display image";
            echo ' - failed to write to "'.$thumbFilename.'"!';
        }
    }

    //insert here copy/pastes of the above for each other image type you want to handle (gif/png/etc...)
}

Easy right? It’s a simple start without any major error checking happening (specifically that if you load something that isn’t an image, it’ll kick up some PHP errors) but it’s the basic starting block.

To make these things reusable, lets wrap it all in a function…


function generateThumbNails($filename, $desiredThumbNailWidth) {
    //check file is actually an image by making sure getimagesize returns an array
    $imageSizeArray = @getimagesize($filename);
    if(is_array($imageSizeArray)) {
        // parse path for the extension
        $fileInfo = pathinfo($filename);

        //check the file type
        if(strtolower($fileInfo['extension']) == 'jpg' ) {
            //GD includes several "imagecreatefrom..." functions for several image types, so use the appropriate function for your image type
            $img = imagecreatefromjpeg($filename);
            list($width, $height) = $imageSizeArray;

            // calculate thumbnail size
            $new_width = $desiredThumbNailWidth;
            $new_height = floor( $height * ( $desiredThumbNailWidth / $width ) );

            // create a new temporary image
            $tmp_img = imagecreatetruecolor( $new_width, $new_height );

            // copy and resize old image into new image
            imagecopyresampled( $tmp_img, $img, 0, 0, 0, 0, $new_width, $new_height, $width, $height );

            // save thumbnail into a file
            $thumbFilename = $fileInfo['dirname'] . '/' . $fileInfo['filename'] . '_' . $desiredThumbNailWidth . '.' . $fileInfo['extension'];
            if(imagejpeg( $tmp_img, $thumbFilename, 80 )) {
                echo " creating thumb/display image";
                echo ' - success!';
            } else {
                echo " creating thumb/display image";
                echo ' - failed to write to "'.$thumbFilename.'"!';
            }
        }

        //insert here copy/pastes of the above for each other image type you want to handle (gif/png/etc...)
    }
}

So there we go, a thumb nail generating function you can apply across multiple images. For example…

foreach(glob("images/*.jpg") as $filename) {
   generateThumbNails($filename, 200);
}

This code will loop through each jpeg in the “images” directory and create a thumbnail of that image. Easy huh?

Next part will look at the upload process. Now we know how to create thumbnails of images, we need images to actually work on!

04
Apr 2012
AUTHOR Barry Parkin
CATEGORY

PHP General, Projects

COMMENTS No Comments

Long time, no update

So I’ve been basically lazy. No excuses, I’ve simply not bothered to update the blog. That’s going to stop now (as in I’m going to start posting).

It’s worth a mention that, while I’ve not been posting, I’ve not actually spent my time doing nothing php based. I’ve had a couple of projects I’ve worked on a bit (more on that in a bit) and attended the PHP UK conference in London, which I highly recommend.

I’ve been missing a bit of passion for working outside of work. Something about spending hours in front of an IDE at work to only get home and open up that same IDE was just sapping me of effort/energy.

So the projects. The last one I worked on was a bit of a waste, but an excellent cautionary tale on the value of planning ahead rather than blindly running into code (like I did). The idea was to crawl a site and check the css used on the site vs what’s in the style sheet. Highlighting unused css would be useful for reducing code and improving performance. Have you seen the size of some css files? Bootstrap is a shocking 81k minified. Yeah I know broadband blah blah blah. Doesn’t change the fact that with mobile device usage growing like it is, every little helps. Drop in page speed as a ranking factor and there’s benefits to it. The reason why it’s a failure destined for the recycle bin? Javascript and forms. Something I realised later on. As soon as javascript gets it’s hands on the page, the whole situation gets complex quickly. Got content hidden behind forms (like validation error feedback) – well a crawler isn’t going to see that either. Does your server display different content to different devices (like smartphones) – well again, bugger. You get the idea. I’m going to go over the code in a future post, maybe someone smarter and more patient will take it further, but for me the sheer number of clauses and edge cases makes me a sad panda.

Second is the image gallery I talked about in my last post months ago. It’s super basic, but functional. I’m tabling up some ideas for improvements, most of which are client side code rather than server side. The idea behind it originally was to be something easy for the wife to use. As a hobby she’s a photographer (the monitor lizard I’ve used as the blog header is hers – sadly has some noise), but isn’t Miss Technical. The goal was intended to be as easy as possible, so I was working a principle of opening an FTP account for her to dump a folder full of images & then the server automagically do the rest. Again, more on that soon™.

Big post. Sorry about that.

13
Mar 2012
AUTHOR Barry Parkin
CATEGORY

PHP General

COMMENTS No Comments

Beta launch and a new project

http://ba.rrypark.in/monitoring/index.php

I’ve finally got around to releasing it for public use. There’s still some bits and pieces to do on the front end. I’ve tested the technical stuff behind the scenes as best as I can. I’ve got a list of features still to be worked on for the front end, some of which are fairly key (ho hum). It’s free to sign up; only an email address and a password is needed. I’ll only contact anyone who signs up when a new release is rolled out (by that I mean when major changes are made).

If you want to give it a little test please do. I’ve set a limit of 5 monitors to start with.

If you want to ask questions or get in touch with me, there’s a contact form once you’ve logged in or you can leave a comment here. For email just address emails to anything @rrypark.in (I prefer just a ‘b’ before the @ as it’s my name!)

For anyone interested, I used twitters bootstrap as a jumping of point for the css. It’s still very bootstrap-esque, but I’m not a designer, so forgive me that. I’ll probably take it a bit further on the next update.

So, now for the new project. It’s something I’ve already made a bit of a start on it. In August of this year my fiancée and I took our immediate family with us to Vegas to get married. It was an exceptional trip and one I’d love to repeat next year (although just the two of us!). When we got back I took the time out to gather up everyone’s SD cards and get a copy of all the pictures. I built up a little php script to traverse a series of directories (let’s call them “albums”) and scan for images. Upon finding a suitable image it creates subdirectory to store thumbnails and subsequently creates them. Of course, when you’ve got >2,000 images to process there’s memory issues ahead. The script limits itself to a set number of thumbnails at a time and then reloads itself to process the next set. It took a little while to complete on my little cheap server, but it got the job done. One last php file then acts as the album gallery creating a list of albums and paginates the image list for a gallery effect.

Yeah it’s a little rudimentary and not the most original script in the world, but it’s simple to deploy (2 php files is all that’s needed). Upload however many photos you want into as many directories as you want and it’ll happily give you a little gallery.

That’s all for now.

11
Oct 2011
AUTHOR Barry Parkin
CATEGORY

Projects

COMMENTS No Comments

I’m still here…

Honestly. Might not seem it, but I am. I need to get back on with this project and start working on other ideas I’ve had. Current state of affairs is that technically, everything works. From a design point of view, there’s much to be done. It’s a basic problem I face; I’m not a designer, I’m a problem solver.

It’s always been a weakness of mine. I just don’t seem to have the eye for design. Not a problem in my day job as we have designers in house. Is a problem for home stuff. I guess it just doesn’t interest me as much as digging into something technical and making it work. Function over form has always been my mantra. I’ve known plenty of applications that are ugly, but because of what they do and how useful they are, you put up with it. Despite the aesthetic short comings, these applications thrive. I’ve never known an application that looks good, but doesn’t function do that well. Design over function works in cars and clothing, but that’s only when they’re non-essential.

I guess where I’m going with this would be once I’m happy (ish) with the design I’ll release the project for general use. I think after that I should probably work on getting a little more exposure to this blog (and thus the monitoring tool) in order to get “real world” feedback.

For now that’s it. I will be returning to coding the monitoring; part of the delay was getting married (yay), partially because of the increased coding work at my day job (couldn’t bear to look at eclipse once I got home), and partially because it was summer and there were other things to do :)

08
Sep 2011
AUTHOR Barry Parkin
CATEGORY

Projects

COMMENTS No Comments

Soon

I’m officially off work for the next two weeks and with a few chores out of the way I’m ready to put the polish on the monitoring and look at launching a small trial just to put some live testers on it. There’s still work to be done and initially I’m not too worried about the design of the interface; function over form. The advantage of a monitoring system is that, unless something goes wrong, chances are you’d only take a peek at the design when setting up the monitoring. Frankly if something was wrong with your server/s and you were logging in regular, then your priorities are a little messed up :)

Expect a few updates over the next two weeks…

19
Apr 2011
AUTHOR Barry Parkin
CATEGORY

Uncategorized

COMMENTS No Comments

Server rebuilt

A quick update, nothing major. I realised that part of the problems I’d been having with sending emails was due to an error I’d made during the configuration of the server. After a quick backup of the code & database I reset the server back to it’s original state and started again. It took a little while to get everything back to how it should be & fixed the problems I was having with email – about 2 hours all in, including the database restore.

Regardless, with this out of the way I can move on with the server monitoring :¬)

06
Apr 2011
AUTHOR Barry Parkin
CATEGORY

Projects

COMMENTS No Comments