I'd like to randomly load images from a directory and have a button somewhere that refreshes the entire page. Here's the current code I have now:
<?php
$a = array();
$dir = '../public/wp-content/uploads/2012/01';
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if (preg_match("/\.png$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpg$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpeg$/", $file)) $a[] = $file;
}
closedir($handle);
}
foreach ($a as $i) {
echo "<img src='" . $dir . '/' . $i . "' />";
}
?>
The problem is it loads all 400,000 images at once. I only want 30 to load. 30 random images from the directory. I tried looking up some code such as modifying the above to this:
<?php
$a = array();
$dir = '../public/wp-content/uploads/2012/01';
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if (preg_match("/\.png$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpg$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpeg$/", $file)) $a[] = $file;
}
closedir($handle);
}
foreach ($a as $i) {
echo "<img src='" . $dir . '/' . $i . "' />";
if (++$i == 2) break;
}
?>
But it seems to do absolutely nothing.. So if someone can help me get 30 random photos from that directory to load and have some type of reload button, that would be of great help.
Thank you in advance
Here is my solution with a cache:
<?php
define('CACHE_FILE', 'mycache.tmp');
define('CACHE_TIME', 20); // 20 seconds (for testing!)
define('IMG_COUNT', 30);
define('IMG_DIR', '../public/wp-content/uploads/2012/01');
/**
* Loads the list (an array) from the cache
* Returns FALSE if the file couldn't be opened or the cache was expired, otherwise the list (as an array) will be returned.
*/
function LoadListFromCache($cacheFile, $cacheTime)
{
if ( file_exists($cacheFile) )
{
$fileHandle = fopen($cacheFile, 'r');
if ( !$fileHandle )
return false;
// Read timestamp (separated by "\n" from the content)
$timestamp = intval( fgets($fileHandle) );
fclose($fileHandle);
// Expired?
if ( $timestamp+$cacheTime > time() )
return false;
else
{
// Unserialize the content!
$content = file_get_contents($cacheFile);
$content = substr( $content, strpos($content, "\n") );
$list = unserialize($content);
return $list;
}
}
return false;
}
/**
* Caches the passed array
* Returns FALSE if the file couldn't be opened, otherwise TRUE.
*/
function SaveListToCache($cacheFile, $list)
{
$fileHandle = fopen($cacheFile, 'w');
if ( $fileHandle === FALSE ) return false;
fwrite($fileHandle, time());
fwrite($fileHandle, "\n");
fwrite($fileHandle, serialize($list));
fclose($fileHandle);
return true;
}
/**
* Generates the list of all image files (png, jpg, jpeg) and caches it.
* Returns the list as an array.
*/
function GenerateList()
{
$a = array();
$dir = IMG_DIR;
if ($handle = opendir($dir))
{
while (false !== ($file = readdir($handle)))
{
if (preg_match("/\.png$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpg$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpeg$/", $file)) $a[] = $file;
}
closedir($handle);
}
SaveListToCache(CACHE_FILE, $a);
return $a;
}
function GetRandomImages($list, $count)
{
$listCount = count($list);
$randomEntries = array();
for ($i=0; $i<$count; $i++)
{
$randomEntries[] = $list[ rand(0, $listCount) ];
}
return $randomEntries;
}
// This code will execute the other functions!
$list = LoadListFromCache(CACHE_FILE, CACHE_TIME);
if ( $list === FALSE )
{
$list = GenerateList();
}
$images = GetRandomImages($list, IMG_COUNT);
foreach ($images as $image)
{
echo '<img src="', IMG_DIR.DIRECTORY_SEPARATOR.$image, '" />';
}
If you have 400,000 images then I think reading the entire directory everytime is going to be an expensive means of showing random images. I would use a database instead and store the file paths in it.
If you want to use your existing code then think of it this way. You have an array of length n containing image names. You want to generate thirty random numbers between 0 and n-1. Then display the image associated with that position in the array. I'm not a php expert, but here is some pseudocode:
$a = array();
$dir = '../public/wp-content/uploads/2012/01';
if (preg_match("/\.png$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpg$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpeg$/", $file)) $a[] = $file;
for ( i=0; i < 30; i++) {
//generate a random number between 0 and N-1
random = rand(0, $a.length - 1);
//display that image in the array
echo "<img src='" . $dir . '/' . $a[random] . "' />";
}
You need to create a new variable for the counter instead of using $i
For example, you can do this instead
$j = 0;
foreach ($a as $i) {
echo "<img src='" . $dir . '/' . $i . "' />";
$j++;
if ($j >= 30)
{
break;
}
}
EDIT: Perhaps for the random part you can first generate a random number between 0 and n-1 where n is the total number of the images and then just echo out the image from the array with the index number.
Instead of using foreach, I think you'll need a for loop instead.
$totalImgs = count($a);
$imgUsed = array();
for ($j = 0; $j < 30; $j++)
{
do
{
$randIndex = mt_rand(0, $totalImgs);
}
while ($imgUsed[$randIndex] === TRUE);
$imgUsed[$randIndex] = TRUE;
echo "<img src='" . $dir . '/' . $a[$randIndex] . "' />";
}
You should maybe only read 30 file from your directory. Stop looking in the directory when readdir return false or your array's length is 30.
This should work
$a = array();
$dir = '../public/wp-content/uploads/2012/01';
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle)) && (count($a) <= 30) {
if (preg_match("/\.png$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpg$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpeg$/", $file)) $a[] = $file;
}
closedir($handle);
}
It may not execute (I didn't try). But the idea is here
For randomize the image: shuffle($a) should do the trick
in simplest way ,
you can use
find , sort , head
commands in linux,in conjuction with PHP's built in
exec()
function to get 30 random image links easily , the folowing snippet lists how to do it
(How to get random 30 image links in an array.)
<?php
$picdir = "directory/containing/pictures"; // directory containing only pictures
exec("find " . $picdir . " | sort -R | head -30 ",$links);
while(list($index,$val) = each($links) ) {
echo "<img src =" .$val . "> <br/>"; // shows image
}
?>
Here $links array contain random 30 image names(from folder) with complete path . This is used with img tag in echo to generate images
Here $picdir has the path of the directory having images and it is assumed that dirrectory is having only image files . in other case its only matter of modifying find command to exclude non image files(such as using grep command to exclude )
Related
I am wanting to calculate the weight of a directory in php, then display the data as per the example below.
Example:
Storage
50 GB (14.12%) of 353 GB used
I have the following function, with which I show in a list the folders that are inside the root.
<?php
$dir = ('D:\data');
echo "Size : " Fsize($dir);
function Fsize($dir)
{
if (is_dir($dir))
{
if ($gd = opendir($dir))
{
$cont = 0;
while (($file = readdir($gd)) !== false)
{
if ($file != "." && $file != ".." )
{
if (is_dir($file))
{
$cont += Fsize($dir."/".$file);
}
else
{
$cont += filesize($dir."/".$file);
echo "file : " . $dir."/".$file . " " . filesize($dir."/".$file)."<br />";
}
}
}
closedir($gd);
}
}
return $cont;
}
?>
The size it shows me of the folder is 3891923, but it is not the real size, when validating the directory the real size is 191791104 bytes
Can you help me, please?
Your test for directory is incorrect here:
if (is_dir($file)) // This test is missing the directory component
{
$cont += Fsize($dir."/".$file);
}
else
Try:
if (is_dir("$dir/$file")) // This test adds the directory path
{
$cont += Fsize($dir."/".$file);
}
else
PHP offers a number of iterators that can simplify operations like this:
$path = "path/to/folder";
$Directory = new RecursiveDirectoryIterator($path);
$Iterator = new RecursiveIteratorIterator($Directory);
$Iterator->setFlags(FilesystemIterator::SKIP_DOTS);
$totalFilesize = 0;
foreach($Iterator as $file){
if ($file->isFile()) {
$totalFilesize += $file->getSize();
}
}
echo "Total: $totalFilesize";
I want to keep only 10 newest files in a folder and delete others.
I created a script that deletes only the oldest ones if a file number is larger than 10.
How can I adapt this script to my needs?
$directory = "/home/dir";
// Returns array of files
$files = scandir($directory);
// Count number of files and store them to variable..
$num_files = count($files)-2;
if($num_files>10){
$smallest_time=INF;
$oldest_file='';
if ($handle = opendir($directory)) {
while (false !== ($file = readdir($handle))) {
$time=filemtime($directory.'/'.$file);
if (is_file($directory.'/'.$file)) {
if ($time < $smallest_time) {
$oldest_file = $file;
$smallest_time = $time;
}
}
}
closedir($handle);
}
echo $oldest_file;
unlink($oldest_file);
}
Basic script to give you the idea. Push all the files with their times into an array, sort it by descending time order and walk trough. if($count > 10) says when the deletion should start, i.e. currently it keeps the newest 10.
<?php
$directory = ".";
$files = array();
foreach(scandir($directory) as $file){
if(is_file($file)) {
//get all the files
$files[$file] = filemtime($file);
}
}
//sort descending by filemtime;
arsort($files);
$count = 1;
foreach ($files as $file => $time){
if($count > 10){
unlink($file);
}
$count++;
}
You could simply sort the result of scandir by the returned files' modification dates:
/**
* #return string[]
*/
function getOldestFiles(string $folderPath, int $count): array
{
// Grab all the filenames
$filenames = #scandir($folderPath);
if ($filenames === false) {
throw new InvalidArgumentException("{$folderPath} is not a valid folder.");
}
// Ignore folders (remove from array)
$filenames = array_filter($filenames, static function (string $filename) use ($folderPath) {
return is_file($folderPath . DIRECTORY_SEPARATOR . $filename);
});
// Sort by ascending last modification date (older first)
usort($filenames, static function (string $file1Name, string $file2Name) use ($folderPath) {
return filemtime($folderPath . DIRECTORY_SEPARATOR . $file1Name) <=> filemtime($folderPath . DIRECTORY_SEPARATOR . $file2Name);
});
// Return the first $count
return array_slice($filenames, 0, $count);
}
Usage:
$folder = '/some/folder';
$oldestFiles = getOldestFiles($folder, 10);
foreach ($oldestFiles as $file) {
unlink($folder . '/' . $file);
}
Note: this is obviously over-commented for the purpose of this answer.
I am using the following script for listing the files in a particular directory as a hyperlink list.
$dir = '.';
$dh = opendir($dir);
$file_count = 0;
while (false !== ($file = readdir($dh))) {
$files[] = $file;
$file_count += 1;
echo $file;
}
for ($x = 0; $x < $file_count; $x += 1) {
echo "<li><a href='$files[$x]'>$files[$x]</a></li>";
}
Please tell us whether the following is possible
1. The file extension should not be displayed
2. I need " " instead of "-" in the file names
If possible how to incorporate it.
Update 2:
Thanks for your kindly help. I followed,
The files names which i have mentioned must not be displayed (like dont display index.php), How do i change the code for this?
<?php
$directory = '';
foreach (glob($directory . "*.php") as $file) {
$parts = pathinfo($file);
$name = preg_replace("/-/", " ", $parts['filename']);
echo "<li>{$name}</li>";
}
?>
Try the following using glob in a loop.
$directory = 'folder/';
foreach (glob($directory . "*.*") as $file) {
$parts = pathinfo($file);
$name = preg_replace("/-/", "", $parts['filename']);
echo "{$name}";
}
Example with your provided code, two lines added pathinfo and a preg_replace.
$dir = '.';
$dh = opendir($dir);
$file_count = 0;
while (false !== ($file = readdir($dh))) {
$files[] = $file;
$file_count += 1;
echo $file;
}
for ($x = 0; $x < $file_count; $x += 1) {
$parts = pathinfo($files[$x]);
$name = preg_replace("/-/", "", $parts['filename']);
echo "<li><a href='$files[$x]'>$name</a></li>";
}
Updated:
The following doesn't show the files called index.php, wtf.php, and hide.php
$directory = 'folder/';
$blacklist = array(
'index',
'wtf',
'hide'
);
foreach (glob($directory . "*.php") as $file) {
$parts = pathinfo($file);
if (!in_array($parts['filename'], $blacklist)) {
$name = preg_replace("/-/", "", $parts['filename']);
echo "<li>{$name}</li>";
}
}
I have tried:
function random_pic($dir = '../myfolder') {
$files = opendir($dir . '/*.*');
$file = array_rand($files);
return $files[$file];
}
This function works using glob() but not opendir.
This returns a failed to open directory error. I guess opendir cannot accept things like *.*? Is it possible to select all files in a folder and randomly choose one?
The opendir() function wont return a list of files/folders. It will only open a handle that can be used by closedir(), readdir() or rewinddir(). The correct usage here would be glob(), but as I see that you don't want that, you could also use scandir() like the following:
<?php
$path = "./";
$files = scandir($path);
shuffle($files);
for($i = 0; ($i < count($files)) && (!is_file($files[$i])); $i++);
echo $files[$i];
?>
I'd happily do the timing to see if this takes longer or if glob() takes longer after you admit that I'm not "wrong."
The following 2 methods make use of opendir to quickly read through a directory and return a random file or directory.
All Benchmarks done use CI3 and are average of 100 pulses. Using WAMP on Win10 Intel i54460 w/ 16GB RAM
Get Random File:
function getRandomFile($path, $type=NULL, $contents=TRUE) {
if (strpos($path, $_SERVER['DOCUMENT_ROOT']) === FALSE) $path = $_SERVER['DOCUMENT_ROOT'] . '/' . $path;
if (is_dir($path)) {
if ($dh = opendir($path)) {
$arr = [];
while (false !== ($file = readdir($dh))) {
// not a directory
if (!is_dir("$path/$file") && !preg_match('/^\.{1,2}$/', $file)) {
// fits file type
if(is_null($type)) $arr[] = $file;
elseif (is_string($type) && preg_match("/\.($type)$/", $file)) $arr[] = $file;
elseif (is_array($type)) {
$type = implode('|', $type);
if (preg_match("/\.($type)$/", $file)) $arr[] = $file;
}
}
}
closedir($dh);
if (!empty($arr)) {
shuffle($arr);
$file = $arr[mt_rand(0, count($arr)-1)];
return empty($contents) ? $file : ($contents == 'path' ? "$path/$file" : file_get_contents($file));
}
}
}
return NULL;
}
Use as simple as:
// Benchmark 0.0018 seconds *
$this->getRandomFile('directoryName');
// would pull random contents of file from given directory
// Benchmark 0.0017 seconds *
$this->getRandomFile('directoryName', 'php');
// |OR|
$this->getRandomFile('directoryName', ['php', 'htm']);
// one gets a random php file
// OR gets random php OR htm file contents
// Benchmark 0.0018 seconds *
$this->getRandomFile('directoryName', NULL, FALSE);
// returns random file name
// Benchmark 0.0019 seconds *
$this->getRandomFile('directoryName', NULL, 'path');
// returns random full file path
Get Random Directory:
function getRandomDir($path, $full=TRUE, $indexOf=NULL) {
if (strpos($path, $_SERVER['DOCUMENT_ROOT']) === FALSE) $path = $_SERVER['DOCUMENT_ROOT'] . '/' . $path;
if (is_dir($path)) {
if ($dh = opendir($path)) {
$arr = [];
while (false !== ($dir = readdir($dh))) {
if (is_dir("$path/$dir") && !preg_match('/^\.{1,2}$/', $dir)) {
if(is_null($indexOf)) $arr[] = $file;
if (is_string($indexOf) && strpos($dir, $indexOf) !== FALSE) $arr[] = $dir;
elseif (is_array($indexOf)) {
$indexOf = implode('|', $indexOf);
if (preg_match("/$indexOf/", $dir)) $arr[] = $dir;
}
}
}
closedir($dh);
if (!empty($arr)) {
shuffle($arr);
$dir = $arr[mt_rand(0, count($arr)-1)];
return $full ? "$path/$dir" : $dir;
}
}
}
return NULL;
}
Use as simple as:
// Benchmark 0.0013 seconds *
$this->getRandomDir('parentDirectoryName');
// returns random full directory path of dirs found in given directory
// Benchmark 0.0015 seconds *
$this->getRandomDir('parentDirectoryName', FALSE);
// returns random directory name
// Benchmark 0.0015 seconds *
$this->getRandomDir('parentDirectoryName', FALSE, 'dirNameContains');
// returns random directory name
Use in Combo Like:
$dir = $this->getRandomDir('dirName');
$file = $this->getRandomFile($dir, 'mp3', FALSE);
// returns a random mp3 file name.
// Could be used to load random song via ajax.
single line
/** getRandomFile(String)
* Simple method for retrieving a random file from a directory
**/
function getRandomFile($path, $type=NULL, $contents=TRUE) { if (strpos($path, $_SERVER['DOCUMENT_ROOT']) === FALSE) $path = $_SERVER['DOCUMENT_ROOT'] . '/' . $path; if (is_dir($path)) { if ($dh = opendir($path)) { $arr = []; while (false !== ($file = readdir($dh))) { if (!is_dir("$path/$file") && !preg_match('/^\.{1,2}$/', $file)) { if(is_null($type)) $arr[] = $file; elseif (is_string($type) && preg_match("/\.($type)$/", $file)) $arr[] = $file; elseif (is_array($type)) { $type = implode('|', $type); if (preg_match("/\.($type)$/", $file)) $arr[] = $file; } } } closedir($dh); if (!empty($arr)) { shuffle($arr); $file = $arr[mt_rand(0, count($arr)-1)]; return empty($contents) ? $file : ($contents == 'path' ? "$path/$file" : file_get_contents($file)); } } } return NULL; }
/** getRandomDir(String)
* Simple method for retrieving a random directory
**/
function getRandomDir($path, $full=TRUE, $indexOf=NULL) { if (strpos($path, $_SERVER['DOCUMENT_ROOT']) === FALSE) $path = $_SERVER['DOCUMENT_ROOT'] . '/' . $path; if (is_dir($path)) { if ($dh = opendir($path)) { $arr = []; while (false !== ($dir = readdir($dh))) { if (is_dir("$path/$dir") && !preg_match('/^\.{1,2}$/', $dir)) { if(is_null($indexOf)) $arr[] = $file; if (is_string($indexOf) && strpos($dir, $indexOf) !== FALSE) $arr[] = $dir; elseif (is_array($indexOf)) { $indexOf = implode('|', $indexOf); if (preg_match("/$indexOf/", $dir)) $arr[] = $dir; } } } closedir($dh); if (!empty($arr)) { shuffle($arr); $dir = $arr[mt_rand(0, count($arr)-1)]; return $full ? "$path/$dir" : $dir; } } } return NULL; }
/* This is only here to make copying easier. */
Just a Note about glob && scandir. I made alternate versions of the getRandomDir using each. Using scandir had very little if any difference in benchmarks (from -.001 to +.003) Using glob was quite noticeably slower! Anywhere from +.5 to +1.100 difference on each call.
The code below is part of a function for grabbing 5 image files from a given directory.
At the moment readdir returns the images 'in the order in which they are stored by the filesystem' as per the spec.
My question is, how can I modify it to get the latest 5 images? Either based on the last_modified date or the filename (which look like 0000009-16-5-2009.png, 0000012-17-5-2009.png, etc.).
if ( $handle = opendir($absolute_dir) )
{
$i = 0;
$image_array = array();
while ( count($image_array) < 5 && ( ($file = readdir($handle)) !== false) )
{
if ( $file != "." && $file != ".." && $file != ".svn" && $file != 'img' )
{
$image_array[$i]['url'] = $relative_dir . $file;
$image_array[$i]['last_modified'] = date ("F d Y H:i:s", filemtime($absolute_dir . '/' . $file));
}
$i++;
}
closedir($handle);
}
If you want to do this entirely in PHP, you must find all the files and their last modification times:
$images = array();
foreach (scandir($folder) as $node) {
$nodePath = $folder . DIRECTORY_SEPARATOR . $node;
if (is_dir($nodePath)) continue;
$images[$nodePath] = filemtime($nodePath);
}
arsort($images);
$newest = array_slice($images, 0, 5);
If you are really only interested in pictures you could use glob() instead of soulmerge's scandir:
$images = array();
foreach (glob("*.{png,jpg,jpeg}", GLOB_BRACE) as $filename) {
$images[$filename] = filemtime($filename);
}
arsort($images);
$newest = array_slice($images, 0, 5);
Or you can create function for the latest 5 files in specified folder.
private function getlatestfivefiles() {
$files = array();
foreach (glob("application/reports/*.*", GLOB_BRACE) as $filename) {
$files[$filename] = filemtime($filename);
}
arsort($files);
$newest = array_slice($files, 0, 5);
return $newest;
}
btw im using CI framework. cheers!