Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

CLab's avatar
Level 3

Generate unique name for file upload

I am uploading a file to the cloud directory and currently I am using the hashName (see here https://laravel.com/docs/10.x/filesystem#other-uploaded-file-information) to get a unique name for the file. All files are stored in one folder, so I am worried that in the future this may result in some conflicts.

My question is therefore, how random is the hashName function of Laravel and is it better to use a UUID generated by the Database in some way?

0 likes
37 replies
LaryAI's avatar
Level 58

The hashName function in Laravel generates a unique name for a file by hashing the file's contents. While it is highly unlikely for two different files to have the same hash, it is not guaranteed to be completely random.

If you want to ensure a truly unique name for each file, you can consider using a UUID (Universally Unique Identifier) generated by the database. Here's an example of how you can generate a UUID using Laravel's Str class:

use Illuminate\Support\Str;

$uuid = Str::uuid()->toString();

You can then use this UUID as the filename for your uploaded file. This approach guarantees uniqueness across all files, even if they are stored in the same folder.

Remember to import the Str class at the top of your file:

use Illuminate\Support\Str;

By using a UUID, you eliminate the possibility of conflicts in the future, as each file will have a unique identifier.

CLab's avatar
Level 3

@LaryAI What is the maximum filename which the hashName function generates?

martinbean's avatar

@clab

This reply was automagically generated by our local A.I. Think of it as a one-off attempt to instantly solve your problem. It will not respond to further replies.

CLab's avatar
Level 3

@martinbean Thanks. Just some feedback - maybe a good idea to not have a reply button for Larry's responses?

Snapey's avatar

Lary suggests the hash is generated from the file contents

As far as I can see, it uses this;

$this->hashName = Str::random(40);

so, just a 40 character random string. The permutations are 5.04 x 10^71 or 504,346,991,352,143,576,208,044,720,854,647,471,565,728,813,056,504,595,316,107,500,909,193,450

Which is more than enough entropy

CLab's avatar
Level 3

@Snapey So here is the thing, I am using file uploads with Livewire and it produces a hashed name when it uploads to S3. Now in some cases, the hashname is definitely greater than 40 characters. example:

adEJc2pHRrf6uW1fvf5ARFthFNmXMp-metac3BlZWR0ZXN0LVNMLTQzNi5wbmc=-.png

68 characters long.

When I upload a file with a longer name / file size it does produce longer strings - I have seen up to 200 characters long.

I assumed livewire would use the same hashName but is it different in some way or am I missing something?

CLab's avatar
Level 3

@Snapey Update: I tracked down how Livewire generates this hash - and it seems it adds the meta of the file to it, which means for very long file names, the hash IDs can get longer.

jlrdw's avatar

Also you could let the user pick a name they can recognize, then:

File name = ann_count of files this user has uploaded + 1_auth::id

  • So ann .jpg is file name
  • 46 is the count + 1
  • 127 is users auth id

So file name to store: ann_46_127.jpg

In one app where I use this it works pretty good.

This prevents any duplicates.

Tray2's avatar

Laravel/PHP already gives it a unique name when you upload the image, so one options is to use that.

foreach ($images as $image) {
            $imageData['original_name'] = $image->getClientOriginalName();
            $imageData['stored_name'] = Str::replace('uploads/', '', $image->store('uploads'));
            Upload::create($imageData);
        }
Snapey's avatar

@Tray2 to be fair, its not a 'unique' name, its just a random name with enough characters making a duplicate a virtual impossibility

1 like
Tray2's avatar

@Snapey Yes, and that is most likely good enough, and if not, a user id and a timestamp is very unique.

$filename = Auth::user()->id . '_' . time();
1 like
tangtang's avatar

@Tray2

not really unique, because in some case the file can be uploaded more than 1 file at once, if using multiple, and the file name will be same each file (even using time).

better add random text or number with specific length will be more suitable.

but just in my opinion. 😁

tangtang's avatar

@Tray2

I agree, adding a file counter in the database as a sequence is a good way (too) to ensure uniqueness. 🍻

Snapey's avatar

@tangtang no the hashname is random name. multiple files at once is NOT a problem and each will get a random filename. no other steps or decoration is required

Snapey's avatar

@tangtang unless you know what you are doing (with atomic transactions) adding a counter derived from the database is an unnecessary and pointless complication

CLab's avatar
Level 3

@Tray2 Sorry - my bad with the English. It should not be a file - user can upload several files. Sometimes the files can have the same name also.

jlrdw's avatar

@Tray2 I may try that as well I kind of like the idea of:

The original file name_userid_timestamp

So far a query that gets their max and adding 1 has gone very fast.

@clab I don't know how you intend to name your files, but I like being able to give the users a way to search for a file by name which is usually the first part of the overall file name.

1 like
Christofer's avatar

@tangtang in some case the file can be uploaded more than 1 file at once

// then also add the file number
$filename = Auth::user()->id . '_' . time() .'_'.$fileNumber;
Christofer's avatar

@tangtang "adding a file counter in the database as a sequence is a good way (too) to ensure uniqueness."

Could be a problem if many users upload at the exact same time and they all query that the MAX(id) is the same number.

Tray2's avatar

@Christofer Not really no, it wouldn't use max id, it would do an insert and get the id, that way it would be unique.

$seq = ImageSquence::create([
		'file_name' => $filename
	]):
Snapey's avatar

can't believe all the bad and off-topic responses to this post. @clab , I would just delete the question.

CLab's avatar
Level 3

@Snapey I don't think I can delete it - only option I have is to edit the question.

jlrdw's avatar

@CLab I prefer:

bobby-at-park_1484_127.png

over

adEJc2pHRrf6uW1fvf5ARFthFNmXMp-metac3BlZWR0ZXN0LVNMLTQzNi5wbmc=-.png

But your app.

CLab's avatar
Level 3

@jlrdw hashing is better for security - so I prefer a hash.

jlrdw's avatar

@CLab a hard to read file name is not how you secure a file you use authorization and store the files out of web all together.

I suggest taking a two or three month study of RBAC and authorization and how to apply the various techniques.

How to secure images and files has been discussed on this forum several times.

Some one can view a regular file name as easy as they can some huge file name.

But if you do store images that are publicly accessible, make sure to let your users know that their uploads are not private, but just a suggestion.

jlrdw's avatar

@CLab from your own question:

https://laracasts.com/discuss/channels/laravel/file-storage-with-original-name-security-issue

It's not a security issue, but can be over rode by accident. That's why I tack on another number, an underscore, and userid.

But generally I do no store files or images in a web folder. Not on php pc now so another example would be:

A quick java example, php you can do similar:

To display:

<img width="80" border="0" src="upload/${row.image}">

But the image is retrieved from file system (out of web)

        try {
            response.setContentType("image/jpeg");
            ServletOutputStream out;
            out = response.getOutputStream();

            String path = "/uploads/img/";
            String filename = path + request.getParameter("imageName");
            FileInputStream fin = new FileInputStream(filename);

            BufferedInputStream bin = new BufferedInputStream(fin);
            BufferedOutputStream bout = new BufferedOutputStream(out);
            int ch = 0;;
            while ((ch = bin.read()) != -1) {
                bout.write(ch);
            }

            bin.close();
            fin.close();
            bout.close();
            out.close();

        } catch (Exception e) {

            response.getOutputStream().println("Exception is ;" + e);

        }

PHP

public function displayImage()
    {
        $basedir = "/uploads";
        $imagedir = Request::input('dir');
        $image = Request::input('img');
        $str = (int) Cln::findId($image, "_", ".");
        If (Auth::id() != $str) {
            exit(0);
        }

        $file = $basedir . '/' . $imagedir . '/' . $image;
        return response()->file($file, array('Content-Type' => 'image/jpeg'));

    }

But, your app store how you want. But I would suggest still putting a clients private images out of web or using something like S3.

S3 is not needed if you learn how to retrieve from another area (file system) on the server that's not assessable from public.

jlrdw's avatar

@CLab did you read his 2 and 3 paragraphs:

Quote

  1. Upload Files Somewhere Else

The XSS and CSRF components of this proof-of-concept relies on this file being uploaded within the application. If you upload all user files elsewhere, such as a separate media domain, block storage (i.e. S3), Content Delivery Network, etc, it limits the exposure. Cookies may no longer be passed through, which removes any privileges the script has over your site.

This is also fantastic protection against potential RCE - if the file is running on someone else’s system, it’s their problem to protect, not yours!

  1. Don’t Make Uploaded Files Directly Accessible If uploaded files cannot be directly accessed in the browser, then they can’t be accessed to be executed. This may not be possible given your file upload needs, but it’s worth thinking about how you can safely store and access untrusted files without users accessing them.

Unquote

Which he said the exact same thing I have told you, don't expose to public. Furthermore I make sure all uploads are scanned for viruses and malware.

getClientOriginalName is fine if other parts are added.

Also getClientOriginalName you could only allow letters. And the other parts are added is from your code.

Also if an upload has a virus, it still has a virus even if you do use UUID.

Edit:

You could even assign an extension in an if construct for one of the allowed file types:

$newname = $filename . $lid . "." . "jpg";

But you are taking security serious, that is good. I wish others would as well, but I don't think most do. They get an easy copy and paste answer and don't give security a second thought.

I did enterprise java ee prior to php, and security is the first thing I am concerned with.

Edit 2

I only do business type apps. I also strip_tags on all form input. I realize a forum site has to have code in the stored database, but a business app doesn't.

Edit 3

But it seems you want to use something like UUID, so just go with that. Just remember it doesn't make a file safe.

1 like
CLab's avatar
Level 3

@jlrdw Thanks for your perspective and sharing your experiences.

CLab's avatar
Level 3

@Snapey I think we are all trying to help and learn together. And the fact they took so much time to explain their point of view is worth respect.

jlrdw's avatar

@CLab also and just FYI there are some good symfony videos on file upload. And remember laravel uses symfony components:

https://symfonycasts.com/screencast/symfony-uploads/file-naming#play

https://symfonycasts.com/screencast/symfony-uploads/upload-in-form#play

They actually cover how to safely have the original name as part of the new file name. Coincidentally they use the same or at least a similar technique I used in java as already discussed here for laravel. But again just FYI and it won't hurt to view the videos.

Jonjie's avatar

You may use this code if you want:

$fileName = time() . rand();

or a bit slower, but stronger:

use Illuminate\Support\Str;

$fileName = md5(time() . rand() . Str::random(40));

Please or to participate in this conversation.