Enforce unique upload file names using django?
Asked Answered
Q

7

70

What's the best way to rename photos with a unique filename on the server as they are uploaded, using django? I want to make sure each name is used only once. Are there any pinax apps that can do this, perhaps with GUID?

Quittor answered 20/4, 2010 at 8:30 Comment(3)
You need a python libary for renaming files, so they are unique?Athlete
See @mlissner's answer. Files using the default Storage class will automatically generate unique names now, no user code is needed anymore. See get_available_nameMalapropism
Django already does that by default. Just name all your image "image.jpg", and starting from the second one they will be automatically named with a pattern such as : image_XpmEQxy.jpg.Yoho
G
168

Use uuid. To tie that into your model see Django documentation for FileField upload_to.

For example in your models.py define the following function:

import uuid
import os

def get_file_path(instance, filename):
    ext = filename.split('.')[-1]
    filename = "%s.%s" % (uuid.uuid4(), ext)
    return os.path.join('uploads/logos', filename)

Then, when defining your FileField/ImageField, specify get_file_path as the upload_to value.

file = models.FileField(upload_to=get_file_path,
                        null=True,
                        blank=True,
                        verbose_name=_(u'Contact list'))
Gnni answered 20/4, 2010 at 18:0 Comment(4)
@Creotiv This isn't changing the upload path, it is replacing the filename with a UUID. This should not cause any migration issues.Menispermaceous
@Creotiv - no, migrations is only tracking how the filename is being generated. This means that migrations knows that there is a function called get_file_path being used. If upload_to=uuid4() was used, that would be the case where the problem you're stating comes into play.Wheatley
getting NameError: name 'get_file_path' is not defined :(Organology
This is actually a great answer, works like a charm - even when you want to upload to different locations programatically based on the uniqueId of the model instance. The only thing you night want to update is: str(uuid.uuid4()) and replace instance with 'self' - working inside the model classBoom
B
20

A better way could be using a common class in your helpers.py. This way you could reuse the random file generator across your apps.

In your helpers.py:

import os
import uuid
from django.utils.deconstruct import deconstructible


@deconstructible
class RandomFileName(object):
    def __init__(self, path):
        self.path = os.path.join(path, "%s%s")

    def __call__(self, _, filename):
        # @note It's up to the validators to check if it's the correct file type in name or if one even exist.
        extension = os.path.splitext(filename)[1]
        return self.path % (uuid.uuid4(), extension)

And then in your model just import the helper class:

from mymodule.helpers import RandomFileName 

And then use it:

logo = models.ImageField(upload_to=RandomFileName('logos'))

Ref: https://coderwall.com/p/hfgoiw/give-imagefield-uploads-a-unique-name-to-avoid-file-overwrites

Batory answered 24/10, 2016 at 0:38 Comment(0)
B
12

As of the writing of this answer it seems like you no longer need to do anything special to make this happen. If you set up a FileField with a static upload_to property, the Django storage system will automatically manage naming so that if a duplicate filename is uploaded, Django will randomly generate a new unique filename for the duplicate.

Works on Django 1.10.

Braden answered 10/4, 2017 at 17:50 Comment(2)
Confirmed that this still works in Django 2.1. This is done automatically in the default Storage class. See get_available_name.Malapropism
Works with django 3.1Rudelson
S
9

Prior to Django 1.6.6, 1.5.9, and 1.4.14, the get_avaialable_name function would automatically give files a unique name by adding an underscore. So, for example, if you save one file "test.jpg" and then another file, "test.jpg" to your server, the first will be called test.jpg, and the second will be called test_1.jpg.

Alas, that turns out to be a vector for DDOSing a machine, by sending it thousands of zero-byte files to store, each one checking thousands of previous files to see what its name should be.

As you'll see in the docs, the new system appends seven random digits after the underscore to fix this problem.

Shaffert answered 20/4, 2010 at 16:26 Comment(0)
W
4

You can write your own FileField and override generate_filename.

For example:

class UniqueNameFileField(FileField):
    def generate_filename(self, instance, filename):
        _, ext = os.path.splitext(filename) 
        name = f'{uuid.uuid4().hex}{ext}'
        return super().generate_filename(instance, name)
Wolof answered 21/3, 2020 at 14:56 Comment(1)
What I like about this one is that I can use it also with a timed based path to further make the entire path unique and not run into folder children size limitations when accessing the file system. With a lot of content files, this can be a real performance problem. Thanks so much for adding this!Hailstone
M
0

How about concatenating the filename with the date / time the photo was uploaded and then using hashlib to create a message digest? That should give you unique filenames.

Alternatively you could re-use a neat little snippet which creates unique filenames and then use the full path to that file as the input to your hash call. That gives you unique constant length strings which you can map to your files.

Mary answered 20/4, 2010 at 9:8 Comment(4)
but,how to do the same name when the same time ?Quittor
You could add the amount of free disk space into the hash string - that should always change when you upload a new file.Mary
@Quittor @Jon Cage you could invent all sorts of ways of making it more likely to be unique and such but there will basically always be some sort of probability of collisions and the idea is just to reduce that probability to an acceptable amount and then handle when it does happenKedgeree
Indeed. The snippet I linked to really would be unique though; the filesystem would garauntee that only one file could be created with the same name. So creating a has on the full path of that should be pretty good.Mary
R
0

django enforce unique filename automatically. if the file already exists, seven unique characters are appended to the filename

tested on django 2.2

Rochkind answered 12/8, 2019 at 16:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.