I-Am-Bot Code, technology and life

21May/090

How to create a Mac OSX finder like download script using PHP

This is a small tutorial/article on how to create a Mac OSX Finder like navigation page and to use it to list and download files that you have.  A screen shot below for people who do not know how it looks. It can be used as a simple yet elegant download script.

500px-osx_finder_columnview

DEMO

DOWNLOAD

Before you start

This script uses PHP/jQuery and a couple of its plugins, and some help from the internet. Since I don't remember all the sources I've used, I'd like to thank the original authors of parts of this script.

Step 1:

We first need to create a list of all files and folders within the current directory. Let us assume your main directory is "download" and you need to list all the files and folders inside it. Create a new file in your favourite text editor and name it as index.php. Copy the following 'scan_directory_recursively' function which returns an array containing a list of all directories and files

function scan_directory_recursively($directory, $filter=FALSE)
{
	if(substr($directory,-1) == '/')
	{
		$directory = substr($directory,0,-1);
	}
	if(!file_exists($directory) || !is_dir($directory))
	{
		return FALSE;
	}elseif(is_readable($directory))
	{
		$directory_list = opendir($directory);
		while($file = readdir($directory_list))
		{
			if($file != '.' && $file != '..')
			{
				$path = $directory.'/'.$file;
				if(is_readable($path))
				{
					$subdirectories = explode('/',$path);
					if(is_dir($path))
					{
						$directory_tree[] = array(
							'path'      => $path,
							'name'      => end($subdirectories),
							'kind'      => 'directory',
							'content'   => scan_directory_recursively($path, $filter));
					}elseif(is_file($path))
					{
						$extension = end(explode('.',end($subdirectories)));
						if($filter === FALSE || $filter == $extension)
						{
							$directory_tree[] = array(
							'path'		=> $path,
							'name'		=> end($subdirectories),
							'extension' => $extension,
							'size'		=> filesize($path),
							'kind'		=> 'file');
						}
					}
				}
			}
		}
		closedir($directory_list);
		return @$directory_tree;
	}else{
		return FALSE;
	}
}
<strong>
 
</strong>

Step 2

Once we have the array containing all the files and folders, we have to create a DIV for the navigation window. Create a div with id="fileTree". Now we need another recursive function that takes the array from the above function as input, and prints the files and folders with the correct nested tags. Remember, to create the correct interface, the output should be in the form of:

<div id="fileTree">
<ul><div>
<li> Folder 1
<ul><div>
<li> Folder 1/A
<ul><div>
<li> Folder 1/A File 1</li>
<li> Folder 1/A File 2</li>
</div></ul>
</li>
<li> Folder 1/B </li>
</li>
<li> Folder 2 </li>
<li> File 1 </li>
<li> File 2 </li>
</div></ul>
</div>

There is an extra div for each column as the columnnav plugin requires it. Each nested <ul><div> creates a new column. We now have to print the files and folders in the correct format. The following function does just that

function create_nested_list($items) {
$out = '<ul>';
global $ignore;
foreach ($items as $item) {
if (in_array($item['name'],$ignore)) {
continue;
}
$out .= '<li>';
if ($item['kind'] == 'directory') {
$out .= '<a href="#">';
$out .= $item['name'];
$out .= '</a>';
if ($item['content']) {
$out .= create_nested_list($item['content']);
}
}
else if($item['kind'] == 'file') {
$out .= '<a href="download.php?file=' . $item['path'] . '">';
$out .= $item['name'];
$out .= '</a>';
}
$out .= '</li>';
}
$out .= '</ul>';
return $out;
}

What it does is for each array item with entry 'content' which means that its a directory and has files within it, the function calls itself, creates another div for the next column, and prints the file. For files, the download link is set as download.php with file name sent as GET parameter. To download a file one will have to double click the entry.

Step 3:

Now that the PHP functions are created, time for some javascript. Include the jquery.js, jquery-columnnav.js and jquery-scrollto.js files to the header of index.php. Now include the following javascript withing the <script type="text/javascript"></script> tags

		$(document).ready( function() {
		$("div#fileTree").columnNavigation({
			containerPosition:"relative",
			containerWidth:"600px",
			containerHeight:"400px",
			containerBackgroundColor:"rgb(255,255,255)",
			containerFontColor:"rgb(50,50,50)",
			columnWidth:250,
			columnFontFamily:"'Helvetica Neue', 'HelveticaNeue', Helvetica, sans-serif",
			columnFontSize:"90%",
			columnSeperatorStyle:"1px solid rgb(220,220,220)",
			columnDeselectFontWeight:"normal",
			columnDeselectColor:"rgb(50,50,50)",
			columnSelectFontWeight:"normal",
			columnSelectColor:"rgb(255,255,255)",
			columnSelectBackgroundColor:"rgb(27,115,213)",
			columnSelectBackgroundImage:"url('Includes/list-selected-background.jpg')",
			columnSelectBackgroundRepeat:"repeat-x",
			columnSelectBackgroundPosition:"top",
			columnItemPadding:"3px 3px 5px 3px",
			columnScrollVelocity:50,
	 	});
	});

The above code sets many options of the columnnav plugin to match it with finder's looks and styling. Play around with the options for a customized look.

Step 4:

Include some CSS for styling

        body
	{
		background:rgb(100,100,100);
		color:rgb(255,255,255);
		font-size:1em;
		font-family:'Helvetica Neue', 'HelveticaNeue', Helvetica, sans-serif;
	}
	div#fileTree a
	{
		padding-left:25px;
        }

Step 5:

Put it all together, and call the PHP functions, print the output

$fileList = scan_directory_recursively('.'); // Get list of files and folders in the current directory recursively
sort($fileList); // Sort the list
$out = create_nested_list($fileList); // Display details of the files/folders with the necessary column formatting
echo $out; // Print the output

Step6:

Now that the main page is ready, create another file download.php to initiate the file download

<?php
$path = getcwd();
// Get name of file to be downloaded
$fname = $_GET['file'];
//Check for various invalid files, and loop holes like ../ and ./
if($fname == '.' || $fname == './' || $fname == "$path/download.php" || $fname == "$path/index.php" || !file_exists($fname) || empty($fname) || preg_match('/\..\/|\.\/\.|assets/',$fname))
{
echo "Invalid File or File Not Specified";
exit(0);
}

// Initiate force file download
@header("Pragma: public");
@header("Expires: 0");
@header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
@header("Content-Type: application/force-download");
@header("Content-Type: application/octet-stream");
@header("Content-Type: application/download");
@header("Content-Disposition: attachment; filename=\"".basename($fname)."\";" );
@header("Content-Transfer-Encoding: binary");
@header("Content-Length: ".filesize($fname));
@readfile($fname);
exit(0);
?>

And Voila! Your files are now displayed in a easy to use page, and all files are forced to download and will NOT open in the browser. You can also link your files directly as "http://yoursite.com/download/download.php?file=<filename>". Customization is very easy, and can produce interesting results! So all the best!

Write your comments/suggestions/corrections below or email them to srinath [AT] iambot [DOT] net

About Srinath

No description. Please complete your profile.
Comments (0) Trackbacks (1)

Leave a comment