简体   繁体   中英

I made an upload script in PHP. How do I avoid overwriting files?

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 .

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

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().

我使用日期和时间函数根据上传时间生成随机文件名。

To check if a file exists, you can use the file_exists function.

To cut the filename, you can use the pathinfo function.

我用

$file_name = time() . "_" . $uploaded_file_name;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM