i have this script which i will post absolutely unmodified:
<?
chdir("data");
$files = glob("*");
shuffle($files);
var_dump($files);
$i=0;
$finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
foreach($files as $file) {
$i++;
$k = $i;
$mime = finfo_file($finfo, $file);
if(strpos($mime,"gif") !== false) {
$ext = "gif";
} else {
$ext = "jpg";
}
if($k < 10) {
$k = "00".$k;
} else if($k < 100) {
$k = "0".$k;
}
$k = $k.".".$ext;
rename($file,$k);
echo $k."\n";
}
the folder data has some image files (jpg and gif) in it.
but when i run it, suddenly a lot of images are just gone!
2/3rd of the images just got deleted...
i don't understand how?
i have an ext3 filesystem and PHP 5.3.2
I can't see anything in the code that would definately cause this behaviour. The most likely cause I could think of is perhaps rename($file,$k); is overwriting files that already exist. You could add the following to rule this out:
if(file_exists($k.".".$ext)) {
$k .= ".0" ;
}
while(file_exists($k.".".$ext)) {
$k .= "0" ;
}
$k = $k.".".$ext;
rename($file,$k);
The other thought I had is that perhaps something is going wrong with the chdir("data") which you could check by inserting the full path before $file and $k when calling the rename. I don't think this is very likely though.
Did you run it twice?
The first time you run it it renames all the images to 0001.jpg - 00nn.jpg. The second time it starts overwriting stuff, because the source names and target names would overlap, e.g. it renames 0042.jpg to 0001.jpg, so the existing 0001.jpg disappears.
Would be good to check if $k exists before renaming $file to $k:
if(!is_file($k)) {
rename($file, $k);
}
Related
I have a number of different hosting accounts set up for clients and need to calculate the amount of storage space being used on each account, which would update regularly.
I have a database set up to record each clients storage usage.
I attempted this first using a PHP file on each account, run by a Cron Job. If run manually by myself, it would output the correct filesize and update the correct size to the database, although when run from the Cron Job, it would output 0.
I then attempted to run this file from a Cron Job from the main account but figured this wouldn't actually work as my hosting would block files from another server and I would end up with the same result as before.
I am now playing around with FTP access to each account from a Cron Job from the main account which looks something like below, the only problem is I don't know how to calculate directory size rather than single file sizes using FTP access, and don't know how to reiterate this way? Hoping somebody might be able to help here before I end up going around in circles?
I will also add the previous first attempt too.
$ftp_conn = ftp_connect($ftp_host, 21, 420) or die("Could not connect to server");
$ftp_login = ftp_login($ftp_conn, $ftp_username, 'mypassword');
$total_size = 0;
$contents = ftp_nlist($ftp_conn, ".");
// output $contents
foreach($contents as $folder){
while($search == true){
if($folder == '..' || $folder == '.'){
} else {
$file = $folder;
$res = ftp_size($ftp_conn, $file);
if ($res != -1) {
$total_size = $total_size + $res;
} else {
$total_size = $total_size;
}
}
}
}
ftp_close($ftp_conn);
This doesn't work as it doesn't calculate folder sizes and I don't know how to open the reiterate using this method?
This second script did work but would only work if opened manually, and return 0 if run by the cron job.
class Directory_Calculator {
function calculate_whole_directory($directory)
{
if ($handle = opendir($directory))
{
$size = 0;
$folders = 0;
$files = 0;
while (false !== ($file = readdir($handle)))
{
if ($file != "." && $file != "..")
{
if(is_dir($directory.$file))
{
$array = $this->calculate_whole_directory($directory.$file.'/');
$size += $array['size'];
$files += $array['files'];
$folders += $array['folders'];
}
else
{
$size += filesize($directory.$file);
$files++;
}
}
}
closedir($handle);
}
$folders++;
return array('size' => $size, 'files' => $files, 'folders' => $folders);
}
}
/* Path to Directory - IMPORTANT: with '/' at the end */
$directory = '../public_html/';
// return an array with: size, total files & folders
$array = $directory_size->size($directory);
$size_of_site = $array['size'];
echo $size_of_site;
Please bare in mind that I am currently testing and none of the MySQLi or PHP scripts are secure yet.
If your server supports MLSD command and you have PHP 7.2 or newer, you can use ftp_mlsd function:
function calculate_whole_directory($ftp_conn, $directory)
{
$files = ftp_mlsd($ftp_conn, $directory) or die("Cannot list $directory");
$result = 0;
foreach ($files as $file)
{
if (($file["type"] == "cdir") || ($file["type"] == "pdir"))
{
$size = 0;
}
else if ($file["type"] == "dir")
{
$size = calculate_whole_directory($ftp_conn, $directory."/".$file["name"]);
}
else
{
$size = intval($file["size"]);
}
$result += $size;
}
return $result;
}
If you do not have PHP 7.2, you can try to implement the MLSD command on your own. For a start, see user comment of the ftp_rawlist command:
https://www.php.net/manual/en/function.ftp-rawlist.php#101071
If you cannot use MLSD, you will particularly have problems telling if an entry is a file or folder. While you can use the ftp_size trick, as you do, calling ftp_size for each entry can take ages.
But if you need to work against one specific FTP server only, you can use ftp_rawlist to retrieve a file listing in a platform-specific format and parse that.
The following code assumes a common *nix format.
function calculate_whole_directory($ftp_conn, $directory)
{
$lines = ftp_rawlist($ftp_conn, $directory) or die("Cannot list $directory");
$result = 0;
foreach ($lines as $line)
{
$tokens = preg_split("/\s+/", $line, 9);
$name = $tokens[8];
if ($tokens[0][0] === 'd')
{
$size = calculate_whole_directory($ftp_conn, "$directory/$name");
}
else
{
$size = intval($tokens[4]);
}
$result += $size;
}
return $result;
}
Based on PHP FTP recursive directory listing.
Regarding cron: I'd guess that the cron does not start your script with a correct working directory, so you calculate a size of a non-existing directory.
Use an absolute path here:
$directory = '../public_html/';
Though you better add some error checking so that you can see yourself what goes wrong.
I want to scan my whatsapp images folder and move all the repeated images to folder named recycle bin to delete them later, here is my code:
<?php
$dir = 'C:\wamp\www\whatsapp';
$files = scandir($dir);
$x = 0;
foreach($files as $f1)
{
$crc1 = strtoupper(dechex(crc32(file_get_contents("whatsapp/".$f1))));
unset($files[$x]);
$j = 0;
foreach($files as $f2)
{
$crc2 = strtoupper(dechex(crc32(file_get_contents("whatsapp/".$f2))));
if($crc1 == $crc2){
rename("whatsapp/".$f2, "recycle bin/".$f2);
unset($files[$j]);
}
$j++;
}
$x++;
}
exit('Done');
does this code seems to be trusted to move only the repeated images without any mistakes?
I've write a small script for your case (but i have not tested its):
<?php
$fileHashes = [];
foreach(scandir('C:\wamp\www\whatsapp') as $file){
$fileHashes["whatsapp/".$file] = sha1(file_get_contents("whatsapp/".$file));
}
$doubles = array_diff_key($fileHashes, array_unique($fileHashes))
foreach($doubles as $file=>$hash){
unlink($file);
}
exit('Done');
So I've killed an entire day trying to do something that would take someone who actually knows how to write php less than 2mins. Frustrating, but I learn by doing and trying to figure things out.
I'll feel like a failure for not getting this, but 8hrs and counting (yeah I know lame) is enough.
Can somebody tell me what's wrong with this equation...
$dir = '../folder';
$images_array = glob($dir.'*.jpg');
$values['options'] = array( '<img src="$images_array"/>');
It's probably obvious, but all I need is for the images in mysite.com/folder to be loaded into the $values['options'] array.
If I simply state the path to a single image in then the image is displayed (obviously because it's not reliant on anything else.)
Thanks.
#hellcode
Sorry about the mess in the 'comment' below your response. Unfortunately I couldn't get this to work? Maybe I need to provide more context.
The images in the folder are going to be used as checkbox items in a form. This was my original code (not working):
add_filter('frm_setup_new_fields_vars', 'frm_set_checked', 20, 2);
function frm_set_checked($values, $field){
if($field->id == 187){
$dir = '../folder';
$images_array = glob($dir.'*.jpg');
$values['options'] = array( '<img src="$images_array"/>');
$values['use_key'] = true;
}
return $values;
}
I added your code like so:
add_filter('frm_setup_new_fields_vars', 'frm_set_checked', 20, 2);
function frm_set_checked($values, $field){
if($field->id == 187){
$dir = '../folder';
$images_array = glob($dir.'*.jpg');
$values['options'] = array();
foreach($images_array as $image) {
$values['options'][] = '<img src="'.$image.'"/>';
}
$values['use_key'] = true;
}
return $values;
}
But it didn't pull the files in unfortunately :(
Try:
$dir = '../folder';
$images_array = glob($dir.'*.jpg');
$values['options'] = array();
foreach($images_array as $image) {
$values['options'][] = '<img src="'.$image.'"/>';
}
Well, one problem may be that the glob() function uses the current directory, which can be anything, unless you use the chdir() function.
One thing that is definitely a problem is that you are using glob()'s return value, $images_array, as a string. Because it is an array that will not work.
Here is something that should work.
// Allowed image formats (also known as a "whitelist")
$allowedFormats = ['jpg', 'jpeg', 'gif', 'png'];
// Array for holding any found images
$foundImages = [];
// Get the real path from the relative path
$path = realpath('../folder');
if ($path === false) {
die('The path does not exist!');
}
// Open a folder handle
$folder = dir($path);
// Read what is in the folder
while (($item = $folder->read()) !== false) {
// .. is the parent folder, . is the current folder
if ($item === '..' or $item === '.') {
continue;
}
// Find the last dot in the filename
// If it was not found then not image file
$lastDot = strrpos($item, '.');
if ($lastDot === false) {
continue;
}
// Get the filetype and make sure it is
// an allowed format
$filetype = substr($item, $lastDot);
if ( ! in_array($filetype, $allowedFormats)) {
continue;
}
// Okay, looks like an image!
$foundImages[] = $item;
}
// Close the folder handle
$folder->close();
I want to grab the first file in a directory, without touching/grabbing all the other files. The filename is unknown.
One very short way could be this, using glob:
$file = array_slice(glob('/directory/*.jpg'), 0, 1);
But if there are a lot of files in that directory, there will be some overhead.
Other ways are answers to this question - but all involve a loop and are also longer then the glob example:
PHP: How can I grab a single file from a directory without scanning entire directory?
Is there a very short and efficient way to solve this?
Probably not totally efficient, but if you only want the FIRST jpg that appears, then
$dh = opendir('directory/');
while($filename = readdir($dh)) {
if (substr($filename, -4) == '.jpg')) {
break;
}
}
Well this is not totally a one-liner, but it is a way to go I believe:
$result = null;
foreach(new FilesystemIterator('directory/') as $file)
{
if($file->isFile() && $file->getExtension() == 'jpg') {
$result = $file->getPathname();
break;
}
}
but why don't you wrap it in a function and use it like get_first_file('directory/') ? It will be a nice and short!
This function will get the first filename of any type.
function get_first_filename ($dir) {
$d = dir($dir);
while ($f = $d->read()){
if (is_file($dir . '/' . $f)) {
$d->close();
return $f;
}
}
}
I have a php script that uploads a zip file of images to a folder. This script recursively seeks out only files in the zip file and places all the files in a single directory on the server.
The problem is certain files get double uploaded. This is not the script's fault, but rather, due to the ridiculousness and inferiority of apple computers, when a mac creates a zip file of images it creates a folder of the images and then another folder with the exact same images only it places "._" in front of the file names. So seeing as how we're not going to be blessed with the disappearance of apple computers anytime soon, I tried to include in my php script a simple function to search for these inferior mac abominations and delete them from the directory. However, php isn't even pulling these files when I use "ftp_nlist".
So my question is: How do I get php to pull these stupid things so I can delete them?
$contents = ftp_nlist($conn_id, '.');
foreach($contents as $key => $value){
echo $key." => ".$value."<BR>";
if(substr($value, 1, 1) == ".") {
if(ftp_delete($conn_id, $value)) {
echo "Deleting $value<BR>";
}
}
echo "<BR>";
}
exit();
EDIT:
So thanks to Stephane's suggestion I was able to come up with this which works
if($zip->open($_FILES['theFile']['tmp_name']) === TRUE){
for($i = 0; $i < $zip->numFiles; $i++) {
$filename = $zip->getNameIndex($i);
$fileinfo = pathinfo($filename);
copy("zip://".$_FILES['theFile']['tmp_name']."#".$filename, $ezPresenter['currentFolder'].'/'.$fileinfo['basename']);
}
$zip->close();
}else{
exit("Could not upload/extract file");
}
$contents = ftp_rawlist($conn_id, '-a');
foreach($contents as $key => $value){
$value = explode(" ", $value);
$value = $value[count($value)-1];
echo $key." => ".$value."<BR>";
if(strpos($value, ".") === false) {
if(ftp_delete($conn_id, $value)) {
echo "Deleting $value<BR>";
}
}
if(substr($value, 0, 2) == "._") {
if(ftp_delete($conn_id, $value)) {
echo "Deleting $value<BR>";
}
}elseif(substr($value, 0, 1) == "." && $value != "." && $value != "..") {
if(ftp_delete($conn_id, $value)) {
echo "Deleting $value<BR>";
}
}
}
Use ftp_rawlist instead.
ftp_rawlist — Returns a detailed list of files in the given directory
ftp_rawlist($connid, "-a");
Argument -a means all as on unix command-line: ls -a.
I ran into this issue before, but I wasn't using ftp_nlist. What I ended up doing was using PHP's ZipArchive to open the zip file and look for (and exclude) the __MACOSX directory. I also ignored zip files where just one directory was inside (so you don't unzip the file and then have two directories deep to get to the data - that always annoys me).
My solution may not be the best for you, as it takes some extra processing, but it worked for me :)
Anyway, without further ado... here is the code I am using. Hopefully it'll be of help to you:
//
// unzip the file
$zip = new ZipArchive;
if ($zip->open($fname) === TRUE) {
//extract zip
$zip->extractTo($dir);
$zip->close();
//detect single dir
$basedir = function($x) use (&$basedir) {
$files = glob($x.'*', GLOB_MARK);
//ignore stupid mac directory
$k = array_search($x.'__MACOSX/',$files);
if($k!==FALSE) {
unset($files[$k]);
$files = array_values($files);
}
if(sizeof($files)==1 && is_dir($files[0]))
return $basedir($files[0]);
return $x;
};
//get root directory that has files in it
$dir = substr($basedir($dir.'/'),0,-1);
//
// here I re-zipped the data from the base directory
// and uploaded this file
//
} else {
//delete the file
unlink($fname);
//
// some other error handling
//
return;
}