Keeping Original Filename for FileField in Django

2019-08-09 20:09发布

问题:

I would like to keep the original file name of an UploadedFile in Django that has its location stored in a FileField. Right now I am observing that if two files have the same name, the first file uploaded keeps its original name but the second time a file with that name is uploaded, it has a random string appended to make the file name unique. One solution is to add an additional field to the model: Django: How to save original filename in FileField? or Saving Original File Name in Django with FileField but these solutions seem suboptimal as they require changing the Model fields.

An alternative would be to prepend a random directory path to the front of the file make sure that in a given directory the file name is unique and allowing the basename to remain unchanged. One way to do this would be to pass in a callable upload_to that does just that. Another option would be to subclass FileField and override get_filename to not strip the input filename to the basename allowing the caller to pass in a filename with a prepended path. The latter option is not ideal if I want to use an ImageField as I would have to subclass that as well.

回答1:

In looking at the code that actually generates the unique filename by appending the random string, it looks like the best solution to this problem might be to subclass the Storage class in-use and override get_available_name method to create unique filenames by prepending a directory rather than post-pending the string to the base name.



回答2:

Sorry for the quick answere, here is another approach to your question : The idea here is to create an unique folder for each uploaded file.

# in your settings.py file
MY_FILE_PATH = 'stored_files/'

The path were your files will be stored : /public/media/stored_files

# somewhere in your project create an utils.py file
import random
try:
    from hashlib import sha1 as sha_constructor
except ImportError:
    from django.utils.hashcompat import sha_constructor


def generate_sha1(string, salt=None):
    """
    Generates a sha1 hash for supplied string.

    :param string:
        The string that needs to be encrypted.

    :param salt:
        Optionally define your own salt. If none is supplied, will use a random
        string of 5 characters.

    :return: Tuple containing the salt and hash.

    """
    if not isinstance(string, (str, unicode)):
         string = str(string)
    if isinstance(string, unicode):
        string = string.encode("utf-8")
    if not salt:
        salt = sha_constructor(str(random.random())).hexdigest()[:5]
    hash = sha_constructor(salt+string).hexdigest()

    return (salt, hash)

In your models.py

from django.conf import settings
from utils.py import generate_sha1

def upload_to_unqiue_folder(instance, filename):
    """
    Uploads a file to an unique generated Path to keep the original filename
    """

    salt, hash = generate_sha1('{}{}'.format(filename, get_datetime_now().now))

    return '%(path)s%(hash_path)s%(filename)s' % {'path': settings.MY_FILE_PATH,
                                               'hash_path': hash[:10],
                                               'filename': filename}



#And then add in your model fileField the uplaod_to function
class MyModel(models.Model):
    file = models.FileField(upload_to=upload_to_unique_folder)

The file will be uploaded to this location :

public/media/stored_file_path/unique_hash_folder/my_file.extention

Note : I got the code from Django userena sources, and adapted it to my needs

Note2 : For more informations take a look at this greate post on Django File upload : File upload example

Have a good day.

Edit : Trying to provide a working solution :)



回答3:

To my understanding, during the form submission/file upload process, you can add form validation functions.

During the validation and cleaning process, you could check that the database does not already have a duplicate name (ie. query to see if that file name exists).

If it is duplicate, you could just rename it xyz_1, xyz_2, etc