In this tutorial we're going to create our own PHP based file browser, so here is what we want to accomplish at the end of this tutorial:
- We want to be able to browse a specific directory on our domain,obviously.
- All of the directory contents must be displayed to the user (files and folders).
- The ability to browse a child directory.
- User must not go beyond a base directory or the allowed directory depth we specify.
- File type filter can be used to filter the contents.
- We should be able to get selected item path, type, name and a reference to the element itself.
What do we need?
PHP 5+ is required in order to get benefit of the scandir function (it can be done in PHP4+, but you need to use opendir and readdir).
JSON support which is PHP 5.2.0+ (or you can build the JSON string yourself).
Here we assumed PHP5.2.0+ is supported.
Also, I'll be using my own ajax snippet, but you can use yours as long as you know where to modify.
the Ajax will return the result in JSON format, you can modify this too if you update corresponding codes.
Note:
If you don't want to read about how it's made, you can jump directly to the How to use it section.
Preparing:
we will work with 3 files:
- index.htm, holds the browser sections.
- broswe.js, contains all needed javascript codes.
- search_dir.php, the server-side script that reads requested directory and return it's contents.
styles.css, ajax.js are included in the attached file.
in addition to above files, we'll create a new folder (images) and add some .png icons to it to represent different file types, I'm using here the famfamfam's Silk pack which can be found here http://www.famfamfam...lab/icons/silk/
And this is how it looks:

index.htm:
Link the style.css file.
Load ajax.js file.
Load browser.js file(we'll work with it later).
let's add the following html elements:
- div, as a container for the file browser.
- text field (txtFilter) to pass wanted file types extensions, I'll put it inside the container, but this is up to you and it's optional.
- button (btnrefresh), to refresh the contents.
- paragraph (pPathDisplay), to display current folder path.
- div (dvContents), a container for the retrieved folder contents.
<!DOCTYPE html> <html> <head> <title>File Browser</title> <link rel="stylesheet" type="text/css" href="styles.css" /> <script type="text/javascript" src="ajax.js"></script> <script type="text/javascript" src="browser.js"></script> </head> <body> <div class="browser"> <p class="pfilter">File types filter <input type="text" id="txtFilter" value=""/> <input type="button" value="Refresh" id="btnrefresh"/> </p> <p id="pPathDisplay" class="pPathDisplay">Loading...</p> <div id="dvContents" class="dvContents"> </div> </div> </body> </html>
styles.css:
All what you need to know is that each row in the content list will have three class attached:
item, (even/odd) and ft_*, where * is the file extension of "folder"
search_dir.php:
Create a function (searchDir) that accepts 4 arguments and assign them default values:
base_dir: string, holds the base directory which is the root directory for users, and they are not allowed to navigate to an upper level directory.
p: string, the folder path we want to browse.
f: string, a comma separated file types to filter the result (user folder if you want to show folders).
allowed_depth: number, the maximum depth level of child directories the user can access.
Declare an empty array (contents) to hold files and folders found as a result of the function call.
Trim all textual arguments.
Set default base_dir to the current if passed as an empty string.
Remove all ./ and ../ sequence from requested folder path which may allow users to navigate to levels upper than base_dir's , and this is what you don't want.
Combine passed folder path with the (base_dir) so user doesn't need to know what is the base directory and use only relative paths.
Check if the new path is a directory and get the directory from the path if it's not.
Make sure the path ends with a slash (/) for later use.
Check if the directory depth is within allowed depth otherwise slice allowed path.
Explode passed filter (f) to an array (filter) of file types (extensions).
Scan the directory for files and return empty contents and return the requested path if failed for some reasons.
<?php
function searchDir($base_dir="./",$p="",$f="",$allowed_depth=-1){
$contents=array();
$base_dir=trim($base_dir);
$p=trim($p);
$f=trim($f);
if($base_dir=="")$base_dir="./";
if(substr($base_dir,-1)!="/")$base_dir.="/";
$p=str_replace(array("../","./"),"",trim($p,"./"));
$p=$base_dir.$p;
if(!is_dir($p))$p=dirname($p);
if(substr($p,-1)!="/")$p.="/";
if($allowed_depth>-1){
$allowed_depth=count(explode("/",$base_dir))+ $allowed_depth-1;
$p=implode("/",array_slice(explode("/",$p),0,$allowed_depth));
if(substr($p,-1)!="/")$p.="/";
}
$filter=($f=="")?array():explode(",",strtolower($f));
$files=@scandir($p);
if(!$files)return array("contents"=>array(),"currentPath"=>$p);
now it's time to process the scan result array (files) using (for) loop.
We get the path to the file by combining the path to the directory with the file name from the (files) array.
The file path may represent a folder too, so we check for this case, cause we'll treat folders differently.
Declare a new Boolean variable (add), this variable we'll be set to true when the file type matches the filter (in some other cases too).
Declare a file type variable (fType).
If the file path represents a file : (not directory)
Take the file extension and apply it to the filter.
Set the variable (add) to true if there is a match.
If the file path represents a directory:
Ignore when file name is (.) which represents the current directory.
Set the variable (add) to true, so all folder we'll be displayed.
Don't (add) the folder if there is a filter and this filter doesn't contain the folder file type (folder).
Get the parent directory path if the file name represents the parent directory (..).
We shouldn't display a link to the parent directory if it refers to directories beyond the base_dir (its path length is less than the base_dir path length), we must stick to the base_dir directory.
Remove the base_dir from file paths and added to the (contents) array if it passes the filter (add==true).
for ($i=0;$i<count($files);$i++){
$fName=$files[$i];
$fPath=$p.$fName;
$isDir=is_dir($fPath);
$add=false;
$fType="folder";
if (!$isDir){
$ft=strtolower(substr($files[$i],strrpos($files[$i],".")+1));
$fType=$ft;
if ($f!=""){
if (in_array($ft,$filter))$add=true;
}else{
$add=true;
}
}else{
if($fName==".")continue;
$add=true;
if ($f!=""){
if (!in_array($fType,$filter))$add=false;
}
if($fName==".."){
if($p==$base_dir){
$add=false;
}else $add=true;
$tempar=explode("/",$fPath);
array_splice($tempar,-2);
$fPath=implode("/",$tempar);
if(strlen($fPath)<= strlen($base_dir))$fPath="";
}
}
if($fPath!="")$fPath=substr($fPath,strlen($base_dir));
if($add)$contents[]=array("fPath"=>$fPath,"fName"=>$fName,"fType"=>$fType);
}
finally, we return the contents array and current path after we remove the base_dir from it.
$p=(strlen($p)<= strlen($base_dir))?$p="":substr($p,strlen($base_dir));
return array("contents"=>$contents,"currentPath"=>$p);
}
The last step is to call this function:
we get the path and filter parameters sent from the client using post method and call (searchDir) function.
and here I used the current directory (./) where the php file is placed for base_dir and set allowed_depth to (-1), but you can change them as needed.
$p=isset($_POST["path"])?$_POST["path"]:"";
$f=isset($_POST["filter"])?$_POST["filter"]:"";
echo json_encode(searchDir("./",$p,$f,-1));
browser.js:
This file contains one main function which accepts one argument(params as Object) and 3 child functions for internal use.
params object can holds the following properties:
contentsDisplay: html element reference, where the folder contents will be listed.
currentPath: String, represents the base_dir related path (you type the path as you are in the base_dir set in the php code).
filter: String/html element reference, that hold comma separated file types (extensions) to be displayed.
loadingMessage: String, the message to be displayed in the pathDisplay while loading folder contents.
pathDisplay: html element reference, this is where the current folder path will appear.
pathMaxDisplay: Number, the maximum characters to be shown in the pathDisplay element.
refreshButton: html element reference, the refresh button.
openFolderonselect: Boolean, open selected folder once selected.
onselect: Function, fired on selecting an item and accepts one object argument which has 4 properties: (type:file type,path:file path,title:file name, item:clicked html element)
data: string, any extra information you may want to pass to the php file (maybe a file browser identity so the base_dir can change accordingly)
Note:
All of these properties are optional, but at least add the contentsDisplay otherwise the result will be included directly into the document's body.
function browser(params){
if(params==null)params={};
if(params.contentsDisplay==null)params.contentsDisplay=document.body;
if(params.currentPath==null)params.currentPath="";
if(params.filter==null)params.filter="";
if(params.loadingMessage==null)params.loadingMessage="Loading...";
if(params.data==null)params.data="";
search function:
Show loading message if possible.
Get the filter.
Send a request to the search_dir.php file and receive the result in the showFiles function
Add click event to the refresh button if assigned.
var search=function(){
if(params.pathDisplay!=null)params.pathDisplay.innerHTML=params.loadingMessage;
var f=typeof(params.filter)=="object"?params.filter.value:params.filter;
var a=new Ajax();
with (a){
Method="POST";
URL="search_dir.php";
Data="path="+params.currentPath+"&filter="+f+"&data="+params.data;
ResponseFormat="json";
ResponseHandler=showFiles;
Send();
}
}
if(params.refreshButton!=null)params.refreshButton.onclick=search;
showFiles function:
Get the current folder path received from server.
Remove unnecessary (./ , ../ , .) from the beginning of the path.
subtract the last pathMaxDisplay if applicable.
Display the path in the pathDisplay.
Clear the contentsDisplay contents.
Declare the oddeven variable to hold the row's odd/even class.
Loop the contents.
Create a paragraph for each element (el) in the contents.
Set the title, fPath, fType attributes.
Set the class name depending on the file type and the odd/even status.
Display the file name in the (el) innerHTML.
Append the paragraph (el) to the (contentsDisplay).
Swap the oddeven variable.
Add click event to the (el) to call the selectItem function
var showFiles=function(res){
if(params.pathDisplay!=null){
var p=res.currentPath;
p=p.replace(/^(\.\.\/|\.\/|\.)*/g,"");
if(params.pathDisplay!=null){
params.pathDisplay.title=p;
if(params.pathMaxDisplay!=null){
if(p.length>params.pathMaxDisplay)p="..."+p.substr(p.length-params.pathMaxDisplay,params.pathMaxDisplay);
}
params.pathDisplay.innerHTML="[Rt:\] "+p;
}
}
params.contentsDisplay.innerHTML="";
var oddeven="odd";
for (i=0;i<res.contents.length;i++){
var f=res.contents[i];
var el=document.createElement("p");
with(el){
setAttribute("title",f.fName);
setAttribute("fPath",f.fPath);
setAttribute("fType",f.fType);
className=oddeven + " item ft_"+f.fType;
innerHTML=f.fName;
}
params.contentsDisplay.appendChild(el);
oddeven=(oddeven=="odd")?"even":"odd";
el.onclick=selectItem;
}
}
selectItem function:
This function will be fired whenever the user clicks an item.
Get the ftype, fpath and title attribute we previously assigned to each element.
Call onselect function if assigned and pass it two arguments: an object has these properties: file type, file path, file title and the html element itself, and the params object.
If selected item is a folder and openSelectedFolder property is set to true then set the browser current path to this folder and open it by calling (search) function.
var selectItem=function(){
var ftype=this.getAttribute("fType");
var fpath=this.getAttribute("fPath");
var ftitle=this.getAttribute("title");
if(params.onselect!=null)params.openFolderonselect=params.onselect({"type":ftype,"path":fpath,"title":ftitle,"item":this},params);
if(params.openFolderonselect==null)params.openFolderonselect=true;
if(ftype=="folder" && params.openFolderonselect){
params.currentPath=fpath;
search();
}
}
and finally call the search function, so users don't need to manually refresh after calling browser function.
search(); }
How to use it ?
Back to index.php file, let's add a script tag right after we load the browser.js file.
We'll add a function (init) to be called on document's load event.
<script type="text/javascript">
function init(){
}
</script>
this function will call the previously created (browser) function .
I'll execute it on the document's load event so we make sure all needed codes and elements are loaded and ready to use;
so update the document's body tag like this:
<body onload="init()">
now, put the following code inside (init) function:
this function call send an object that specify the contents display area, refresh button, path display area, element holds the file types filter and sets the current path to images folder which must be a child directory of the base_dir specified inside the php file.
browser({
contentsDisplay:document.getElementById("dvContents"),
refreshButton:document.getElementById("btnrefresh"),
pathDisplay:document.getElementById("pPathDisplay"),
filter:document.getElementById("txtFilter"),
currentPath:"images"
});
Notes:
You can set the openFolderonselect params property to false if you don't want at all to open selected folder.
openFolderonselect:true
Or you can created onselect handler to process selected item before you decide what to do with this item.
if this function returns false and it was a folder, selected folder will not be opened, but it will if the function returns true.
this function overwrites the openFolderonselect property when not returning null.
onselect:function(item,params){
if(item.type=="folder")
return confirm("Do you want to open this Folder : "+item.path);
else
alert("You selected :"+item.path)
}
make sure to add a comma after each property declaration to params argument except for the last one.
since we pass params object to the onselect function, it's possible for you to add a custom properties to it when calling the browser function to be used later from within onselect function using its params argument.
all files attached here
browser.zip (16.22K)
Number of downloads: 1342
you can also try it here, I set the currentPath to images and added a testing junk folder it
Note:
Posted first time as an answer here http://www.dreaminco...ost__p__1357461
but it's better now, at least this what I think
Hope it helps






MultiQuote





|