3

I'm creating a web application that allows users to upload images to a server.

How can I prevent duplicate image file names when a user uploads images?

How can I change the filename to a unique name that is no duplicate of any previous image's name (or next image that might be uploaded)?

Editing :

if i user primary key autoincrement as image filename for each images ? is it will be useful ?

Dev777
  • 141
  • 1
  • 6
  • Plenty of ways to do this, I prefer using a GUID with `com_create_guid()` - this is a Windows only function but check the comments for a Linux replacement: http://php.net/com_create_guid – scrowler Feb 25 '14 at 01:36
  • possible duplicate of [php Unique filename when uploading](http://stackoverflow.com/questions/8563958/php-unique-filename-when-uploading) – s1lence Feb 25 '14 at 01:53
  • @s1lence, as stated in many articles, merely using `uniqid()` is not enough to guarantee uniqueness of a file name. For example, if you deploy multiple instances of this application, which can upload files to the same directory, relying only on `uniqid()` (even if using more entropy) will run the risk of name collision. Therefore, the "possible duplicate" you are referring to is simply a low quality answer in this regard. My two cents. – Yanick Rochon Feb 25 '14 at 03:20
  • That's exactly the point I made with my answer below. But you're right, even though the other question is an exact duplicate, neither of them have a satisfying accepted answer yet... – s1lence Feb 25 '14 at 04:43

3 Answers3

4

I already answered such question before. And I updated my code to add more randomness (entropy) to the generated id.

This class generate pseudo-unique, non-sequential, non-numeric IDs.

class IdGenerator {

    static private function _nextChar() {
        return base_convert(mt_rand(0, 35), 10, 36);
    }

    static public function generate() {
        $parts = explode('.', uniqid('', true));

        $id = str_pad(base_convert($parts[0], 16, 2), 56, mt_rand(0, 1), STR_PAD_LEFT)
            . str_pad(base_convert($parts[1], 10, 2), 32, mt_rand(0, 1), STR_PAD_LEFT);
        $id = str_pad($id, strlen($id) + (8 - (strlen($id) % 8)), mt_rand(0, 1), STR_PAD_BOTH);

        $chunks = str_split($id, 8);

        $id = array();
        foreach ($chunks as $key => $chunk) {
            if ($key & 1) {  // odd
                array_unshift($id, $chunk);
            } else {         // even
                array_push($id, $chunk);
            }
        }

        // add random seeds
        $prefix = str_pad(base_convert(mt_rand(), 10, 36), 6, self::_nextChar(), STR_PAD_BOTH);
        $id = str_pad(base_convert(implode($id), 2, 36), 19, self::_nextChar(), STR_PAD_BOTH);
        $suffix = str_pad(base_convert(mt_rand(), 10, 36), 6, self::_nextChar(), STR_PAD_BOTH);

        return $prefix . self::_nextChar() . $id . $suffix;
    }
}

If you execute this script

header('Content-type: text/plain; charset=utf-8');

for ($i=0; $i<10; $i++) {
    $uid = IdGenerator::generate();

    echo $uid . " = " . strlen($uid) . "\n";
}

You will get something like this :

x0i8eea3c8kw4lgudmoss4c4w03db6wl = 32
byqrfgc6hilr9d1ot4wow8gw4syugtvz = 32
ta075al22zp3v6awtlw4kgkk446mjbiv = 32
hqqa90p27e9desx99q8skokcc46fujx4 = 32
uqc000q7g20l1k9zlwko80gsow5e59e7 = 32
gxx2r5d5oa0p8iykvc4ckgc4kc0teekv = 32
ayysoos5ltfua3d0m80ccocc0kcfhqyb = 32
dtj31vi4tzmh6lhk1iccc0os4cgsze1e = 32
fvn41hh2gnk6lbrq4w0wwgko8k5ihda8 = 32
oxamsba3qh0ro6xehkw8cg400s10tiyq = 32

** Edit **

So, why all this? Why not just use uniqid()? Because uniqid() is sequential and is predictable. Because you need to add more entropy. This class not only use uniqid() "more entropy" argument, it also uses mt_rand() to pad the generated values with it. The class provided here will also always generate a 32 bytes (256-bits) string.

How random this function is? To have a duplicate ID, one would need to call uniqid() at the exact same time, and mt_rand() would need to return the exact same random values in the same order... seven times in a row. The bottom line is that it is quite random.

** Edit 2 **

You may also be interested by a pure PHP UUID implementation.

** Edit 3 **

The problem with using a Primary Key (PK) as unique file name is that it is predictable. If you intend to serve these files directly from URI routes, then a generated non-sequential value is safer. If you intend to serve these files otherwise, then these files will have to be assigned some unique key anyhow... and this key cannot be sequential for the exact same reasons. So, regardless the use case, having a non-sequential unique key as file name is a good idea.

Community
  • 1
  • 1
Yanick Rochon
  • 51,409
  • 25
  • 133
  • 214
  • 1
    but may one of value can be duplicate ? – Dev777 Feb 25 '14 at 01:45
  • Not very likely since it is based on PHP's `uniqid` function. Read [here](http://stackoverflow.com/questions/4070110/how-unique-is-uniqid) about uniqueness. For your need (file uploads), I'd say chances that you run into a race condition is unlikely. – Yanick Rochon Feb 25 '14 at 01:48
  • Obviously this could still yield duplicates. The chances that this happens however are quite small. If you simply add a check to see if there is already a file with the same name and then select a different one, you'd be on the safe side (while still having a small probability for duplicates due to concurrent uploads) – s1lence Feb 25 '14 at 01:51
  • @s1lence because `uniqid()` is sequencial and may offer some predictability. This function does not. **Note**: I'm updating the answer to make it more "random" :) – Yanick Rochon Feb 25 '14 at 01:57
0

The easiest way that guarantees unique filenames is to use a simple sequence that increases for every new image.

Sequence

If you use a database like MySQL to store additional information to the images you could just use the automatically assigned ID of a primary key column with AUTO_INCREMENT or you can also just store the current sequence value in a simple text file.

Note, that the option using a file to store the current value is dangerous because by simply accessing it, two concurrent file uploads could yield the same filename. You could use file locks (see docs of flock) to circumvent this but that could be inefficient.

Hashing Functions

Using hashing functions is not guaranteed to yield unique ids since two different inputs can generate the same output (with some usually low probability). Also the timestamp only might be to inaccurate and therefore result in the same filename if two files are uploaded in a very small timeframe.

Funk Forty Niner
  • 74,450
  • 15
  • 68
  • 141
s1lence
  • 2,188
  • 2
  • 16
  • 34
-1

You can use the MD5 of the current timestamp * random for example

$date = date('Y-m-d H:i:s');
$filename = md5(uniqid($date, true) * rand()) . ".png";

Also have a look here: PHP: How to generate a random, unique, alphanumeric string?

And here: http://php.net/uniqid

Community
  • 1
  • 1
Mazzy
  • 1,901
  • 2
  • 16
  • 36
  • @Dev777 Also have a look at that other stackoverflow question, that should get you where you need, they answer it way better the everyone here! :) – Mazzy Feb 25 '14 at 01:38