This question already has answers here:
How to generate a random, unique, alphanumeric string?
(31 answers)
Closed 9 years ago.
How would I go about creating a random string of text for use with file names?
I am uploading photos and renaming them upon completion. All photos are going to be stored in one directory so their filenames need to be unique.
Is there a standard way of doing this?
Is there a way to check if the filename already exists before trying to overwrite?
This is for a single user environment (myself) to show my personal photos on my website however I would like to automate it a little. I don't need to worry about two users trying to upload and generating the same filename at the same time but I do want to check if it exists already.
I know how to upload the file, and I know how to generate random strings, but I want to know if there is a standard way of doing it.
The proper way to do this is to use PHP's tempnam() function. It creates a file in the specified directory with a guaranteed unique name, so you don't have to worry about randomness or overwriting an existing file:
$filename = tempnam('/path/to/storage/directory', '');
unlink($filename);
move_uploaded_file($_FILES['file']['tmp_name'], $filename);
function random_string($length) {
$key = '';
$keys = array_merge(range(0, 9), range('a', 'z'));
for ($i = 0; $i < $length; $i++) {
$key .= $keys[array_rand($keys)];
}
return $key;
}
echo random_string(50);
Example output:
zsd16xzv3jsytnp87tk7ygv73k8zmr0ekh6ly7mxaeyeh46oe8
EDIT
Make this unique in a directory, changes to function here:
function random_filename($length, $directory = '', $extension = '')
{
// default to this files directory if empty...
$dir = !empty($directory) && is_dir($directory) ? $directory : dirname(__FILE__);
do {
$key = '';
$keys = array_merge(range(0, 9), range('a', 'z'));
for ($i = 0; $i < $length; $i++) {
$key .= $keys[array_rand($keys)];
}
} while (file_exists($dir . '/' . $key . (!empty($extension) ? '.' . $extension : '')));
return $key . (!empty($extension) ? '.' . $extension : '');
}
// Checks in the directory of where this file is located.
echo random_filename(50);
// Checks in a user-supplied directory...
echo random_filename(50, '/ServerRoot/mysite/myfiles');
// Checks in current directory of php file, with zip extension...
echo random_filename(50, '', 'zip');
Hope this is what you are looking for:-
<?php
function generateFileName()
{
$chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789_";
$name = "";
for($i=0; $i<12; $i++)
$name.= $chars[rand(0,strlen($chars))];
return $name;
}
//get a random name of the file here
$fileName = generateName();
//what we need to do is scan the directory for existence of the current filename
$files = scandir(dirname(__FILE__).'/images');//assumed images are stored in images directory of the current directory
$temp = $fileName.'.'.$_FILES['assumed']['type'];//add extension to randomly generated image name
for($i = 0; $i<count($files); $i++)
if($temp==$files[$i] && !is_dir($files[$i]))
{
$fileName .= "_1.".$_FILES['assumed']['type'];
break;
}
unset($temp);
unset($files);
//now you can upload an image in the directory with a random unique file name as you required
move_uploaded_file($_FILES['assumed']['tmp_name'],"images/".$fileName);
unset($fileName);
?>
Related
This question already has answers here:
PHP code to exclude index.php using glob
(3 answers)
Closed 7 years ago.
I'm having issues on figuring this out. How do I exclude some files (eg: index.php) from the folder/ to be counted as. Any suggestions?
<?php
$dir = "folder/";
$count = 0;
$files = glob($dir . "*.{php}",GLOB_BRACE);
if($files){$count = count($files);}
echo'You have: '.$count.' files.';
?>
I'm guessing you will need to list these files at some point as well, so this will build a new array of "approved" file names.
$dir = "";
$files = glob($dir . "*.{php}",GLOB_BRACE);
$realfiles = array();
$ignore = array('index.php','otherfile.pdf'); // List of ignored file names
foreach($files as $f) {
if(!in_array($f, $ignore)) { // This file is not in our ignore list
$realfiles[]=$f; // Add this file name to our realfiles array
}
}
echo 'You have: '.count($realfiles).' files.';
print_r($realfiles);
You can do something like this:
$dir = "/folder";
$count = 0;
$files = glob($dir . "*.{php}",GLOB_BRACE);
for($i=0;$i<count($files);$i++){
if(strpos($files[$i], "index.php")){
unset($files[$i]);
}
}
if($files){$count = count($files);}
echo'You have: '.$count.' files.';
strpos will Find the numeric position of the first occurrence of needle in the haystack string.
After getting this just unset that array index from files array.
I am writing an application in PHP where the user submits a form of data and a file name is chosen based off of the data, like so:
$filename = "./savelocation/".$name."_".$identification."_".$date.'.txt';
I am trying to use the file_exists() function to check to see if a file with the same name exists. If it does, the final name is changed to prevent overwriting the submitted form data. Here is my implementation:
$file = "./savelocation/".$name."_".$identification."_".$date.'.txt';
$file = preg_replace('/\s+/', '', $file);
$filepath = "./savelocation/".$name."_".$identification."_".$date.'.txt';
if(file_exists($filepath))
{
$file = "./savelocation/"."INVALIDFILE".'.txt';
}
This prevents people from overwriting applications by changing the name to a single file which acts as the 'default file' in which it doesn't matter if it is overwritten. However, I know this is wrong. My logic was that the if statement would return true, which would execute the code inside of the statement changing the file name to the 'default file'. Is this even a good way to prevent duplicate submissions?
Try this...if there is a match on the file name, break from the loop and redirect
$userFile = $name."_".$identification."_".$date.'.txt;
$fileArray = glob('./savelocation/*');
$arrCount = count($fileArray);
$i = 1;
$msg = null;
foreach ($fileArray as $FA) {
$fileSubstring = str_replace("\.\/savelocation\/", "", $FA);
if ($i > $arrCount) {
break;
} else if ($userFile === $fileSubstring) {
$msg = 'repeat';
break;
} else null;
$i++;
}
if (isset($msg)) header('location: PageThatChastisesUser.php');
Alternatively, if you tweak your code a bit to change your file name, this should work:
if(file_exists($file)) {
$file = str_replace("\.txt", "duplicate\.txt", $file);
}
Change the file name in a way that identifies itself to you as a duplicate.
Here's one way of doing it:
$file = "./savelocation/".$name."_".$identification."_".$date.'.txt';
$file = preg_replace('/\s+/', '', $filen);
$filepath = "./savelocation/".$name."_".$identification."_".$date.'.txt';
$i = 1;
while(file_exists($filepath))
{
$filepath = "./savelocation/".$name."_".$identification."_".$date.'_'.$i.'.txt';
$i++;
}
I looked at a few other questions mentioning tempnam() in the context of unique file naming.
I was left a bit unclear on whether the file names will be truly unique.
Let's say that we have a file upload script, that moves and renames the files to a permanent directory.
What I want to ask, is: Will the file name always be unique, when used like this:
$tmp_name = tempnam($dir, '');
unlink($tmp_name);
copy($uploaded_file, "$tmp_name.$ext");
As cantsay suggested, I made a php script to look for identical values.
function tempnam_tst() {
for ($i=0; $i < 250000 ; $i++) {
$tmp_name = tempnam('/tmp/', '');
unlink($tmp_name);
$arr[$i] = $tmp_name;
}
return array_intersect($arr, array_unique(array_diff_key($arr, array_unique($arr))));
}
$arr = array();
do {
$arr = tempnam_tst();
} while ( empty($arr) );
echo 'Matching items (case-sensitive):<br>';
echo '<pre>';
print_r($arr);
echo '</pre>';
Result:
Matching items (case-sensitive):
Array
(
[59996] => /tmp/8wB6RI
[92722] => /tmp/KnFtJa
[130990] => /tmp/KnFtJa
[173696] => /tmp/8wB6RI
)
From what I can see, tempnam() does not always generate an unique name.
Try this->
$uploadPath = "/upload/";
$fileName = time().$_FILES['file_name']['name'];
$tempName = $_FILES['file_name']['tmp_name'];
move_uploaded_file($tempName,$uploadPath.$fileName);
This will upload unique file in upload folder.
So I have this app that processes CSV files. I have a line of code to load the file.
$myFile = "data/FrontlineSMS_Message_Export_20120721.csv"; //The name of the CSV file
$fh = fopen($myFile, 'r'); //Open the file
I would like to find a way in which I could look in the data directory and get the newest file (they all have date tags so they would be in order inside of data) and set the name equal to $myFile.
I really couldn't find and understand the documentation of php directories so any helpful resources would be appreciated as well. Thank you.
Here's an attempt using scandir, assuming the only files in the directory have timestamped filenames:
$files = scandir('data', SCANDIR_SORT_DESCENDING);
$newest_file = $files[0];
We first list all files in the directory in descending order, then, whichever one is first in that list has the "greatest" filename — and therefore the greatest timestamp value — and is therefore the newest.
Note that scandir was added in PHP 5, but its documentation page shows how to implement that behavior in PHP 4.
For a search with wildcard you can use:
<?php
$path = "/var/www/html/*";
$latest_ctime = 0;
$latest_filename = '';
$files = glob($path);
foreach($files as $file)
{
if (is_file($file) && filectime($file) > $latest_ctime)
{
$latest_ctime = filectime($file);
$latest_filename = $file;
}
}
return $latest_filename;
?>
My solution, improved solution from Max Hofmann:
$ret = [];
$dir = Yii::getAlias("#app") . "/web/uploads/problem-letters/{$this->id}"; // set directory in question
if(is_dir($dir)) {
$ret = array_diff(scandir($dir), array(".", "..")); // get all files in dir as array and remove . and .. from it
}
usort($ret, function ($a, $b) use ($dir) {
if(filectime($dir . "/" . $a) < filectime($dir . "/" . $b)) {
return -1;
} else if(filectime($dir . "/" . $a) == filectime($dir . "/" . $b)) {
return 0;
} else {
return 1;
}
}); // sort array by file creation time, older first
echo $ret[count($ret)-1]; // filename of last created file
Here's an example where I felt more confident in using my own validator rather than simply relying on a timestamp with scandir().
In this context, I want to check if my server has a more recent file version than the client's version. So I compare version numbers from the file names.
$clientAppVersion = "1.0.5";
$latestVersionFileName = "";
$directory = "../../download/updates/darwin/"
$arrayOfFiles = scandir($directory);
foreach ($arrayOfFiles as $file) {
if (is_file($directory . $file)) {
// Your custom code here... For example:
$serverFileVersion = getVersionNumberFromFileName($file);
if (isVersionNumberGreater($serverFileVersion, $clientAppVersion)) {
$latestVersionFileName = $file;
}
}
}
// function declarations in my php file (used in the forEach loop)
function getVersionNumberFromFileName($fileName) {
// extract the version number with regEx replacement
return preg_replace("/Finance D - Tenue de livres-darwin-(x64|arm64)-|\.zip/", "", $fileName);
}
function removeAllNonDigits($semanticVersionString) {
// use regex replacement to keep only numeric values in the semantic version string
return preg_replace("/\D+/", "", $semanticVersionString);
}
function isVersionNumberGreater($serverFileVersion, $clientFileVersion): bool {
// receives two semantic versions (1.0.4) and compares their numeric value (104)
// true when server version is greater than client version (105 > 104)
return removeAllNonDigits($serverFileVersion) > removeAllNonDigits($clientFileVersion);
}
Using this manual comparison instead of a timestamp I can achieve a more surgical result. I hope this can give you some useful ideas if you have a similar requirement.
(PS: I took time to post because I was not satisfied with the answers I found relating to the specific requirement I had. Please be kind I'm also not very used to StackOverflow - Thanks!)
So I am creating trying to create a PHP script where the client can create a folder with a 10 digit name of random letters and numbers, and than save the document they are currently working on into that folder. Its like a JSfiddle where you can save what you are currently working on and it makes a random folder. My issue is that it wont create my directory, and the idea is correct, and it should work. However, PHP isn't saving an Error Log so I cannot identify the issue. Here's what I got so far.
PHP
save_functions.php
<?php
function genRandomString() {
$length = 10;
$characters = "0123456789abcdefghijklmnopqrstuvwxyz";
$string = '';
for ($p = 0; $p < $length; $p++) {
$string .= $characters[mt_rand(0, strlen($characters))];
}
return $string;
}
<?php
function createFolder() {
$folderName = genRandomString(); //Make a random name for the folder
$goTo = '../$folderName';//Path to folder
while(is_dir($goTo)==true){ //Check if a folder with that name exists
$folderName = genRandomString();
$goTo = '../$folderName';
}
mkdir($goTo,7777); //Make a directory with that name at $goTo
return $goTo; //Return the path to the folder
}
?>
create_files.php
<?php
include('save_functions.php');//Include those functions
$doc = $_POST['doc'];//Get contents of the file
$folder = createFolder();//Make the folder with that random name
$docName = '$folder/style.css';//Create the css file
$dh = fopen($docName, 'w+') or die("can't open file");//Open or create the file
fwrite($dh, $doc);//Overwrite contents of the file
fclose($dh);//Close handler
?>
The call to mkdir($goTo,7777) has wrong mode, this is usually octal and not decimal or hex. 7777 is 017141 in octal and thus tries to set non-existent bits. Try the usual 0777.
But why don't you just use tempnam() or tmpfile() in your case?