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.
Related
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.
I tried to create a folder to store all my images but it will not upload on its specific upload folder inside my installed theme.
Here is my code:
if(isset($_FILES['file']['tmp_name']))
{
$num_files = count($_FILES['file']['tmp_name']);//count file upload
for($i=0; $i<$num_files; $i++)
{
if(!is_uploaded_file($_FILES['file']['tmp_name'][$i]))
{
echo "no file upload!!";
}else
{
if(#copy($_FILES['file']['tmp_name'][$i], "/upload/".$_FILES['file']['name'][$i]))
{
$path = "upload/".$_FILES['file']['name'][$i];
//$sql = "insert into tblImage value ('".$path."')";
}else
{
echo "cant upload";
}
}
}
}
It's difficult to figure out exactly what went wrong because I don't know your setup, but try these things:
It's inadvisable to use #copy in this case, especially while you're trying to figure out what's going wrong. Copy will return false on failures and show other error messages in exceptional situations, unless you prefix it with #. If your disk is full, for example, it'll fail silently. Just copy is likely more useful for this.
Your file naming is, at the very least, difficult to decipher. Specifically, what paths are you using? PHP file operations like copy require precise path information. Your path, /upload/, which is upload/ later, refers to something relative from some arbitrary place, that may change. To solve this, figure out a good absolute path. If you're uploading to WP uploads, start with wp_upload_dir and build from there.
Unless you absolutely need a copy of the file from the original location later, move it instead of copying it. Remember that you'd be moving it to the uploads folder, so it'd still be available later, just somewhere else. This also avails you to move_uploaded_file().
There are some great comments (like from #kabiir) about how to debug what you've got. It's hard to figure out exactly what's going wrong without some debugging information.
Try:
if(isset($_FILES['file']['tmp_name'])) {
for ($i = 0; $i < count($_FILES['file']['tmp_name']); $i++) {
$file = $_FILE['file']['tmp_name'][$i];
error_log('source: ' . $file);
if (is_uploaded_file($file) && isset($_FILE['file']['name'][$i])) {
$name = $_FILE['file']['name'][$i];
error_log('uploaded name: ' . $name);
$destination = trailingslashit(wp_upload_dir()) . $name;
error_log('destination: ' . $destination);
if (!move_uploaded_file($source, $destination)) {
echo 'failed to move file.';
continue;
}
// Now $destination contains where the file was moved to.
}
}
}
This code will output to your log (or wherever else you have error logging pointed to) the source file+path, the uploaded file name and destination file+path, per file.
Unless there's something very special you're doing, wp_handle_upload() should take care of all your needs without rewriting this. When it's done, it calls the wp_handle_upload filter. Hook to the end of that filter's chain to do your SQL or whatever else. There are also many plugins that do what you're trying to do and save you from having to code it yourself.
As a side note, you ought not create SQL entirely by hand in WP. Use the wpdb class, with the $wpdb->prepare() function. This also allows you to use $wpdb->prefix, which is the prefix for your table. (You've prefixed your table and created it according to WP standards, right?)
I have developed an API integration, It contains multiple image/file uploads. There are name conflicts if multiple users uploads file with the same name.
I've planned to create dynamic folders with random names to fix this issue (as temp folder & will delete once the process has been done). Are there any methods/techniques available to generate random folders in PHP?
For things like this, I've found the php function uniqid to be useful.
Basically, something like this:
$dirname = uniqid();
mkdir($dirname);
And then just move the uploaded file to this directory.
Edit: Forgot to mention, the directory name is not random, but is guaranteed to be unique, which seems to be what you need.
I guess that it is best to have a function that tries creating random folders (and verifying if it is successful) until it succeeds.
This one has no race conditions nor is it depending on faith in uniqid() providing a name that has not already been used as a name in the tempdir.
function tempdir() {
$name = uniqid();
$dir = sys_get_temp_dir() . '/' . $name;
if(!file_exists($dir)) {
if(mkdir($dir) === true) {
return $dir;
}
}
return tempdir();
}
Yes its possible using mkdir()Example
<?php
mkdir("/path/to/my/dir", 0700);
?>
For more check this
http://php.net/manual/en/function.mkdir.php
I have the php code below which help me get a photo's thumbnail image path in a script
It will take a supplied value like this from a mysql DB '2/34/12/thepicture.jpg'
It will then turn it into this '2/34/12/thepicture_thumb1.jpg'
I am sure there is a better performance way of doing this and I am open to any help please
Also on a page with 50 user's this would run 50 times to get 50 different photos
// the photo has it is pulled from the DB, it has the folders and filename as 1
$photo_url = '2/34/12/thepicture_thumb1.jpg';
//build the full photo filepath
$file = $site_path. 'images/userphoto/' . $photo_url;
// make sure file name is not empty and the file exist
if ($photo_url != '' && file_exists($file)) {
//get file info
$fil_ext1 = pathinfo($file);
$fil_ext = $fil_ext1['extension'];
$fil_explode = '.' . $fil_ext;
$arr = explode($fil_explode, $photo_url);
// add "_thumb" or else "_thumb1" inbetween
// the file name and the file extension 2/45/12/photo.jpg becomes 2/45/12/photo_thumb1.jpg
$pic1 = $arr[0] . "_thumb" . $fil_explode;
//make sure the thumbnail image exist
if (file_exists("images/userphoto/" . $pic1)) {
//retunr the thumbnail image url
$img_name = $pic1;
}
}
1 thing I am curious about is how it uses pathinfo() to get the files extension, since the extension will always be 3 digits, would other methods of getting this value better performance?
Is there a performance problem with this code, or are you just optimizing prematurely? Unless the performance is bad enough to be a usability issue and the profiler tells you that this code is to blame, there are much more pressing issues with this code.
To answer the question: "How can I improve this PHP code?" Add whitespace.
Performance-wise, if you're calling built-in PHP functions the performance is excellent because you're running compiled code behind the scenes.
Of course, calling all these functions when you don't need to isn't a good idea. In your case, the pathinfo function returns the various paths you need. You call the explode function on the original name when you can build the file name like this (note, the 'filename' is only available since PHP 5.2):
$fInfo = pathinfo($file);
$thumb_name = $fInfo['dirname'] . '/' . $fInfo['filename'] . '_thumb' . $fInfo['extension'];
If you don't have PHP 5.2, then the simplest way is to ignore that function and use strrpos and substr:
// gets the position of the last dot
$lastDot = strrpos($file, '.');
// first bit gets everything before the dot,
// second gets everything from the dot onwards
$thumbName = substr($file, 0, $lastDot) . '_thumb1' . substr($file, $lastDot);
The best optimization for this code is to increase it's readability:
// make sure file name is not empty and the file exist
if ( $photo_url != '' && file_exists($file) ) {
// Get information about the file path
$path_info = pathinfo($file);
// determine the thumbnail name
// add "_thumb" or else "_thumb1" inbetween
// the file name and the file extension 2/45/12/photo.jpg
// becomes 2/45/12/photo_thumb.jpg
$pic1 = "{$path_info['dirname']}/{$path_info['basename']}_thumb.{$fil_ext}";
// if this calculated thumbnail file exists, use it in place of
// the image name
if ( file_exists( "images/userphoto/" . $pic1 ) ) {
$img_name = $pic1;
}
}
I have broken up the components of the function using line breaks, and used the information returned from pathinfo() to simplify the process of determining the thumbnail name.
Updated to incorporate feedback from #DisgruntledGoat
Why are you even concerned about the performance of this function? Assuming you call it only once (say, when the "main" filename is generated) and store the result, its runtime should be essentially zero compared to DB and filesystem access. If you're calling it on every access to re-compute the thumbnail path, well, that's wasteful but it's still not going to be significantly impacting your runtime.
Now, if you want it to look nicer and be more maintainable, that's a worthwhile goal.
The easiest way to fix this is to thumbnail all user profile pics before hand and keep it around so you don't keep resizing.
$img_name = preg_replace('/^(.*)(\..*?)$/', '\1_thumb\2', $file);
Edit: bbcode disappeared with \.
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.