Stephan-v Stephan-v - 1 month ago 18
PHP Question

How to prevent duplicate filenames in laravel?

This is the code I have in a store method from one of my controllers.

// Get the file object from the user input.
$file = Request::file('filefield');

// Get the filename by referring to the input object
$fileName = $file->getClientOriginalName();

if (!Storage::exists($fileName))
{
Storage::disk('local')->put($fileName, File::get($file));
} else {
return 'Hey this file exist already';
}


It works fine but the problem that I had, is that it allowed for duplicate filenames and the file obviously wouldn't upload. I tried fixing it with this which is fine so far.

Now I'm guessing if I want a user to upload a filename with an identical name to one that I already have I need to append something like a number to the filename.

My question is what is the best way of going about this in laravel?

Help is much appreciated.

Answer

There are a few things you could do.

If the original filename already exists, the following code will look for an integer in it before the extension. If there isn't one it adds one. Then it increments this number and checks until such a filename doesn't exist.

if (Storage::exists($fileName)) {
    // Split filename into parts
    $pathInfo = pathinfo($fileName);
    $extension = isset($pathInfo['extension']) ? ('.' . $pathInfo['extension']) : '';

    // Look for a number before the extension; add one if there isn't already
    if (preg_match('/(.*?)(\d+)$/', $pathInfo['filename'], $match)) {
        // Have a number; increment it
        $base = $match[1];
        $number = intVal($match[2]);
    } else {
        // No number; add one
        $base = $pathInfo['filename'];
        $number = 0;
    }

    // Choose a name with an incremented number until a file with that name 
    // doesn't exist
    do {
        $fileName = $pathInfo['dirname'] . DIRECTORY_SEPARATOR . $base . ++$number . $extension;
    } while (Storage::exists($fileName));
}

// Store the file
Storage::disk('local')->put($fileName, File::get($file));

Alternatively, you could generate a unique string, such as with uniqid, and append this to the original filename (or use it alone). If you do that you have a chance of collision so approaching zero that many would say it's not even worth checking if a file with that name already exists or not.

Either way (more so with the first example) there is a chance that another process makes the file between this one verifying it doesn't exist and then writing the file. If this happens you could have data loss. There are some ways to mitigate this possibility, such as by instead using tempnam.