I’m having a file upload system in my website for a long time. During the time, I realized that many users upload files that contain same names but are different from each other. As the names are same, the upload fails!
I’ve decided not to use the $_FILES['file']['name'] for the filenames again.
In my case, a random echo of a $_FILES['file']['tmp_name'] looks like /tmp/phpynIF3G. I’m going to use the $_FILES['file']['tmp_name'] for the file name.
For example, at first it was $filename = $_FILES['file']['name']; and now it’d be $filename = str_replace("/tmp/php","",$_FILES['file']['tmp_name']).".jpg";.
So, when the file will be successfully uploaded to my /e/ directory, it can be accessed by a url which is https://sitename/e/ynIF3G.jpg.
If Php has already used the /tmp/phpynIF3G once, will it ever use the same tmp_name for an other file again?
If each user uploads a file named XYZ.jpg, and if you want them to not be overwriten, you have to either validate if the file exists and change its name or separate where to save the files.
Usually, theres a file directory that can you structure your files, something like user/userid/files/XYZ.jpg.
$_FILES['file']['tmp_name'] is a name, temporary for that matter, randomly generated. It is possible, altho unlikely, that it may repeat itself eventually.
May I suggest, if you don't want to take the approach of having folders to separate the files, you use:
while (file_exists($_FILES['file']['name'])) {
//If exists, then just add a random string to the end of the file
//This is just an example of an approach, what this will do is make your file look like: filename.jpg.aksjwq,
//The rest is homework for you, to work an improved solution, I'm just here to give you the idea :P
$_FILES['file']['name'] .= randomString(6);
}
function randomString($length = 6) {
$str = "";
$characters = array_merge(range('A','Z'), range('a','z'), range('0','9'));
$max = count($characters) - 1;
for ($i = 0; $i < $length; $i++) {
$rand = mt_rand(0, $max);
$str .= $characters[$rand];
}
return $str;
}
Yes, it's a possibility, tmp != unique
Avoid using tmp_name as its name, either use its real name or hash the file to get its checksum (> sha256) then use that, if you want it short just store it as an auto-incremented number, then use base_convert(), or a lib like hashids.
Related
I have a file hosting website and everything works except one thing. I cannot upload two files with the same name. I want to be able to overright the name of a new file with a format like file_2.ext i believe this can be done with the file_exists() function but I am having trouble getting it to work. I will post the system I use to get files to upload below.
System:
if (move_uploaded_file($tmp_location, $location . $name)) {
echo'<br>Upload was successful';
}
I tried doing:
if (isset($_POST['submit_file']) &&!file_exists($name)) {
move_uploaded_file($tmp_location, $location . $name)
echo'<br>Upload was successful';
} else if (isset($_POST['submit_file']) &&file_exists($name)) {
!move_uploaded_file($tmp_location, $location . $name);
echo 'File exists';
}
The method above did not post the error message.
Try something like this. You will need to find a unique filename. You don't want to let it go indefinitely though, so add a limit to it.
function get_unique_filename($filename) {
if (!file_exists($filename)) return $filename;
$limit = 200;
$path = dirname($filename).DIRECTORY_SEPARATOR;
$basename = basename($filename);
$parts = explode('.', $basename);
$ext = array_pop($parts);
$base = implode('.', $parts);
for ($i=2; $i<$limit+2; $i++)
if (!file_exists($path.$base.'_'.$i.'.'.$ext))
return $path.$base.'_'.$i.'.'.$ext;
return false;
}
if (isset($_POST['submit_file'])) {
$new_path = get_unique_filename($location.$name);
if ($new_path && !move_uploaded_file($tmp_location, $new_path))
echo 'Could not upload file.'
else
echo '<br/>Upload was successful';
}
The way you're tackling it is wrong in my opinion , even if you need the user to have the "original" filename, you can't show them "file2.ext" if they uploaded "file.ext", that's just confusing and feels so wrong, take this example
Your server has files named file.ext, file2.ext and file4.ext and
your user uploads a file named file.ext, how would you handle that ? will you iterate over the pattern file*.ext and then add one to the biggest number ? or look for holes
and name it file3.ext ?, or will you append a random number to the file ? how will you handle collisions ? keep looping till it works ? it's just too much pain and might end up making your server really busy if you handle lots of uploads !
What I would do, is use uniqid for filenames on your filesystem, then have a DB that links original filenames to the UUIDs.
This way, all your users are able to upload an file named "file.ext", also it's cleaner to handle files since everything will be unique and their names will have the same length, and no need to worry about filenames in who knows what encoding, etc..
It'll also give you a bonus of being able to change UUIDs and show the same filenames to the user without worrying what they see, this way you're having a layer between the actual files on your FS and what the user perceives files.
When someone uploads an image to my server, it uploads as the name they have for the image on their computer. I want the image name to become a short number as the name instead of the name, because when someone uploads an image as the same name, it overwrites it.
Here is my script where the names are defined:
if(isset($_FILES['fileup']) && strlen($_FILES['fileup']['name']) > 1) {
$uploadpath = $uploadpath . basename( $_FILES['fileup']['name']);
$sepext = explode('.', strtolower($_FILES['fileup']['name']));
$type = end($sepext);
list($width, $height) = getimagesize($_FILES['fileup']['tmp_name']);
$err = '';
This is a piece of (working) code I wrote a few years ago, that I still use to this day.
You probably may have to modify it to fit your script as well as the position in the script and the path.
However, I did replace my own original name (photo) with your fileup name.
Plus there are a few other goodies in there you probably could use, somewhere down the line.
$file_name = $HTTP_POST_FILES['fileup']['name'][0];
// should there be spaces in the uploaded file, replace them with an underscore
$file_name = str_replace(" ","_",$file_name);
$random_digit = substr(number_format(time() * rand(),0,'',''),0,10);
$new_file_name=$random_digit._.$file_name;
$path1= "./uploads/".$new_file_name;
// you can also use (my original write):
// copy($HTTP_POST_FILES['fileup']['tmp_name'][0], $path1);
move_uploaded_file($HTTP_POST_FILES['fileup']['tmp_name'][0], $path1);
$path_to_file_web = "http://www.example.com/uploads/$new_file_name";
The original script that I wrote was used on an older server with an older PHP version.
However, copy() will still work.
I do something like this:
i am just add some random string at beginning of file name
$new=generate_name(10);
$name=$new.$name;
Function generate_name();
function generate_name($long){
$cadena="[^A-Z0-9]";
return substr(eregi_replace($cadena, "", md5(rand())) .
eregi_replace($cadena, "", md5(rand())) .
eregi_replace($cadena, "", md5(rand())),
0, $long);
}
and generate this name:
03f396a35902-06-2013.csv
from 02-06-2013.csv file.
Not only do you not have to use the name provided when uploading a file, you shouldn't do so - like any user input, it is untrusted and could be used as a security hole.
So all you need to do is generate a random image name, and ensure it is not one that you've already used (e.g. by checking with file_exists() and using a do ... while loop to keep generating random names until that returns false). Then use that as the argument to move_uploaded_file(), which is the correct function to use for putting the file into a final location.
If you don't mind too much about the length and format, uniqid() is a good way to generate your filenames, because it is based on the current time so is unlikely to produce collisions.
I am completely new to PHP so forgive me if this question seems very rudimentry. And thank you in advance.
I need to include a jpg that is generated from a webcam on another page. However I need to include only the latest jpg file. Unfortunately the webcam creates a unique filename for each jpg. How can I use include or another function to only include the latest image file?
(Typically the filename is something like this 2011011011231101.jpg where it stands for year_month_date_timestamp).
Easy way is to get the latest image with the help of the below code
$path = "/path/to/my/dir";
$latest_ctime = 0;
$latest_filename = '';
$d = dir($path);
while (false !== ($entry = $d->read())) {
$filepath = "{$path}/{$entry}";
// could do also other checks than just checking whether the entry is a file
if (is_file($filepath) && filectime($filepath) > $latest_ctime) {
$latest_ctime = filectime($filepath);
$latest_filename = $entry;
}
}
}
// now $latest_filename contains the filename of the newest file
give the source of latest image to <img> tag
Since the images are named via pattern which relates to the date, you should be able to just use:
$imgs = glob('C:\images\*.jpg');
rsort($imgs);
$newestImage = $imgs[0];
This is fairly straightforward, since your file names are in order.
The first thing you need is a list of files in the directory. The readdir (doc) function is what you are looking for. Example script that uses it: http://www.liamdelahunty.com/tips/php_list_a_directory.php
Once you have that, use substr() (doc) to chop off the file name extensions.
You're left with an array of numbers, essentially. From here, do a sort (doc) and specify the SORT_NUMERIC flag. Grab the number on the end, stick a .jpg back on it, and you have the last file.
Alternate Solution: Read the timestamps of files to get the last one. This would generally be a better answer, but perhaps not in your situation if you plan to edit any of the files.
I guess you will have to know a way to determine what the latest image file is called. Maybe you can make a textfile or something where every time a new image is created the webcam writes the latest filename in the text file (so the only text in the text file is the file name of the latest image file if it makes any sense). Of course you will have to have access to the script that generates the php file.
addition to #ken 's post, it's probably sorting alphabetically instead of numerically. perhaps you could try:
$imgs = glob('C:\images\*.jpg');
rsort($imgs, SORT_NUMERIC);
$newestImage = $imgs[0];
I've made an image upload script using the move_uploaded_file function. This function seems to overwrite any preexisting file with the new one. So, I need to check if the target location already has a file. If it does then I need to append something to the filename(before the extension so that the file name is still valid) so the filename is unique. I'd like to have the change be minimal instead of something like appending the datetime, if possible.
How can I do this with PHP?
When uploading files I will nearly always rename them. Typically there will be some kind of database record for that file. I use the ID of that to guarantee uniqueness of the file. Sometimes I'll even store what the client's original filename was in the database too but I'll never keep it or the temporary name because there is no guarantee that information is good, that your OS will support it or that it's unique (which is your issue).
So just rename it to some scheme of your own devising. That's my advice.
If you don't have any database reference, then you could use file_exists() for this but there's no guarantee that between the time of checking if something exists and moving it that something else won't use that same filename that you'll then overwrite. This is a classic race condition.
http://us3.php.net/manual/en/function.file-exists.php
Don't use file_exists() for the reason that it returns true (on *nix systems at least, since directories are specialized files) if the value is a directory. Use is_file() instead.
For example, say something fails and you have a string like:
$path = "/path/to/file/" . $file; // Assuming $file is an empty value, if something failed for example
if ( true === file_exists($path) ) { echo "This returns true"; }
if ( true === is_file($path) ) { echo "You will not read this"; }
It's caused a few problems in the past for me, so I always use is_file() rather than file_exists().
I use date and time functions to generate a random file name based on the time of upload.
Let's assume you are submitting a file from a form where you have an input named incomingfile like this:
<input type="file" id="incomingfile" name="incomingfile" />
First of all I use to "depure" the filename and copy it from the default temporary directory to a temporary directory. This is necessary to deal with special characters. I had troubles when I didn't adopt this practice.
$new_depured_filename = strtolower(preg_replace('/[^a-zA-Z0-9_ -.]/s', '_', $_FILES["incomingfile"]["name"]));
copy($_FILES["incomingfile"]["tmp_name"], 'my_temp_directory/'.$new_depured_filename);
With the following piece of code I check if the file exists, if so, I find a new name and finally copy it. For example if I want to write a file called myimage.jpg and it already exists I rename the pending file to myimage__000.jpg. If this exists as well I rename the pending file to myimage__001.jpg and so on until I find a non-existing filename.
$i=0; // A counter for the tail to append to the filename
$new_filename = $new_depured_filename;
$new_filepath='myfiles/music/'.$new_filename;
while(file_exists($new_filepath)) {
$tail = str_pad((string) $i, 3, "0", STR_PAD_LEFT); // Converts the integer in $i to a string of 3 characters with left zero fill.
$fileinfos = pathinfo($new_filepath); // Gathers some infos about the file
if($i>0) { // If we aren't at the first while cycle (where you have the filename without any added strings) then delete the tail (like "__000") from the filename to add another one later (otherwise you'd have filenames like myfile__000__001__002__003.jpg)
$previous_tail = str_pad((string) $i-1, 3, "0", STR_PAD_LEFT);
$new_filename = str_replace('__'.$previous_tail,"",$new_filename);
}
$new_filename = str_replace('.'.$fileinfos['extension'],"",$new_filename); // Deletes the extension
$new_filename = $new_filename.'__'.$tail.'.'.$fileinfos['extension']; // Append our tail and the extension
$new_filepath = 'myfiles/music/'.$new_filename; // Crea il nuovo percorso
$i++;
}
copy('my_temp_directory/'.$new_depured_filename, $new_filepath); // Finally we copy the file to its destination directory
unlink('my_temp_directory/'.$new_depured_filename); // and delete the temporary one
Used functions:
strtolower
preg_replace
copy
file_exists
str_pad
pathinfo
str_replace
unlink
To check if a file exists, you can use the file_exists function.
To cut the filename, you can use the pathinfo function.
I use
$file_name = time() . "_" . $uploaded_file_name;
I need to convert some files to PDF and then attach them to an email. I'm using Pear Mail for the email side of it and that's fine (mostly--still working out some issues) but as part of this I need to create temporary files. Now I could use the tempnam() function but it sounds like it creates a file on the filesystem, which isn't what I want.
I just want a name in the temporary file system (using sys_get_temp_dir()) that won't clash with someone else running the same script of the same user invoking the script more than once.
Suggestions?
I've used uniqid() in the past to generate a unique filename, but not actually create the file.
$filename = uniqid(rand(), true) . '.pdf';
The first parameter can be anything you want, but I used rand() here to make it even a bit more random. Using a set prefix, you could further avoid collisions with other temp files in the system.
$filename = uniqid('MyApp', true) . '.pdf';
From there, you just create the file. If all else fails, put it in a while loop and keep generating it until you get one that works.
while (true) {
$filename = uniqid('MyApp', true) . '.pdf';
if (!file_exists(sys_get_temp_dir() . $filename)) break;
}
Seriously, use tempnam(). Yes, this creates the file, but this is a very intentional security measure designed to prevent another process on your system from "stealing" your filename and causing your process to overwrite files you don't want.
I.e., consider this sequence:
You generate a random name.
You check the file system to make sure it doesn't exist. If it does, repeat the previous step.
Another, evil, process creates a file with the same name as a hard link to a file Mr Evil wants you to accidentally overwrite.
You open the file, thinking you're creating the file rather than opening an existing one in write mode and you start writing to it.
You just overwrote something important.
PHP's tempnam() actually calls the system's mkstemp under the hood (that's for Linux... substitute the "best practice" function for other OSs), which goes through a process like this:
Pick a filename
Create the file with restrictive permissions, inside a directory that prevents others from removing files it doesn't own (that's what the sticky-bit does on /var/tmp and /tmp)
Confirms that the file created still has the restrictive permissions.
If any of the above fails, try again with a different name.
Returns the filename created.
Now, you can do all of those things yourself, but why, when "the proper function" does everything that's required to create secure temporary files, and that almost always involves creating an empty file for you.
Exceptions:
You're creating a temporary file in a directory that only your process can create/delete files in.
Create a randomly generated temporary directory, which only your process can create/delete files in.
Another alternative based on #Lusid answer with a failover of max execution time:
// Max exectution time of 10 seconds.
$maxExecTime = time() + 10;
$isUnique = false;
while (time() !== $maxExecTime) {
// Unique file name
$uniqueFileName = uniqid(mt_rand(), true) . '.pdf';
if (!file_exists(sys_get_temp_dir() . $uniqueFileName)){
$isUnique = true;
break;
}
}
if($isUnique){
// Save your file with your unique name
}else{
// Time limit was exceeded without finding a unique name
}
Note:
I prefer to use mt_rand instead of rand because the first function use Mersenne Twister algorithm and it's faster than the second (LCG).
More info:
http://php.net/manual/en/function.uniqid.php
http://php.net/manual/en/function.mt-rand.php
http://php.net/manual/en/function.time.php
Consider using an uuid for the filename. Consider the uniqid function.
http://php.net/uniqid
You could use part of the date and time in order to create a unique file name, that way it isn't duplicated when invoked more than once.
I recomend you to use the PHP function
http://www.php.net/tempnam
$file=tempnam('tmpdownload', 'Ergebnis_'.date(Y.m.d).'_').'.pdf';
echo $file;
/var/www/html/tmpdownload/Ergebnis_20071004_Xbn6PY.pdf
Or
http://www.php.net/tmpfile
<?php
$temp = tmpfile();
fwrite($temp, "writing to tempfile");
fseek($temp, 0);
echo fread($temp, 1024);
fclose($temp); // this removes the file
?>
Better use Unix timestamp with the user id.
$filename='file_'.time().'_'.$id.'.jepg';
My idea is to use a recursive function to see if the filename exists, and if it does, iterate to the next integer:
function check_revision($filename, $rev){
$new_filename = $filename."-".$rev.".csv";
if(file_exists($new_filename)){
$new_filename = check_revision($filename, $rev++);
}
return $new_filename;
}
$revision = 1;
$filename = "whatever";
$filename = check_revision($filename, $revision);
function gen_filename($dir) {
if (!#is_dir($dir)) {
#mkdir($dir, 0777, true);
}
$filename = uniqid('MyApp.', true).".pdf";
if (#is_file($dir."/".$filename)) {
return $this->gen_filename($dir);
}
return $filename;
}
Update 2020
hash_file('md5', $file_pathname)
This way you will prevent duplications.