如何编写一个自定义的文件存储类

如果你需要提供自定义文件储存功能——一个普通的例子是,把文件储存在远程系统中——自定义一个存储类可以完成这一任务来完成。下面是需要完成的具体步骤:

  1. 你自定义的存储系统必须为 Django.core.files.storage.Storage 的一个子类:

    from django.core.files.storage import Storage
    
    
    class MyStorage(Storage):
        ...
    
  2. Django 必须能以无参数实例化你的存储系统。意味着所有配置都应从 django.conf.settings 配置中获取:

    from django.conf import settings
    from django.core.files.storage import Storage
    
    
    class MyStorage(Storage):
        def __init__(self, option=None):
            if not option:
                option = settings.CUSTOM_STORAGE_OPTIONS
            ...
    
  3. 在你的存储类中,除了其他自定义的方法外,还必须实现 _open() 以及 _save() 等其他适合你的存储类的方法。关于这些方法,详情请查看下面的信息。

    另外,如果你的类提供了本地文件存储,它必须重写 path() 方法。

  4. 您的存储类必须是 deconstructible,以便在迁移中的字段上使用它时可以序列化。 只要你的字段有自己的参数 serializable,你可以使用 django.utils.deconstruct.deconstructible 类装饰器(这是 Django 在 FileSystemStorage 上使用的)。

默认情况下,下面的方法将引发一个 NotImplementedError 的错误,并且通常它们必须被重写。

可是,记住并非所有这些方法都是需要的,并且可能故意被省略。正因为如此,让每个方法未实现并仍然拥有一个可用储存是可能的。

举例来说,如果列出某些存储后端的内容被证明代价会很昂贵,那么您可以决定不实现 Storage.listdir() 方法。

另一个例子是只处理写入文件的后端。在这种情况下,你不需要实现上述任何方法。

最终,你决定实现这些方法中的哪一个。一些方法未实现结果会生成部分(可能会损坏的)接口。

你可能也经常会用到专为自定义存储对象设计的钩子函数。他们是:

_open(name, mode='rb')

要求

Called by Storage.open(), this is the actual mechanism the storage class uses to open the file. This must return a File object, though in most cases, you'll want to return some subclass here that implements logic specific to the backend storage system. The FileNotFoundError exception should be raised when a file doesn't exist.

_save(name, content)

被称为 Storage.save()。这个 name 会早已经历 get_valid_name()get_available_name(),并且 content 将会成为 File 对象自身。

Should return the actual name of the file saved (usually the name passed in, but if the storage needs to change the file name return the new name instead).

get_valid_name(name)

返回适用于底层存储系统的文件名。 传递给此方法的 name 参数既不是发送给服务器的原始文件名,如果 upload_to 是可调用的,则在删除任何路径信息后由该方法返回的文件名。 重写此操作可以自定义如何将非标准字符转换为安全文件名。

“Storage”上提供的代码仅保留原始文件名中的字母数字字符,句点和下划线,并删除其他所有内容。

get_alternative_name(file_root, file_ext)

将会在 file_rootfile_ext 这两个参数的基础上返回另一个文件名。默认情况下,在执行前一个下划线再加上 7 个由字母和数字组成的字符串将将会追加到原来的文件名后。

get_available_name(name, max_length=None)

返回存储机制中可用的文件名,可能会考虑提供的文件名。 根据上述 get_valid_name() 方法,传递给此方法的 name 参数已经被清除为一个对存储系统有效的文件名。

返回的文件名的长度不会超过 max_length,如果该参数被提供的话。若找不到一个可用的独一无二的文件名,则抛出一个 SuspiciousFileOperation 异常。

假如已经存在名为 name 的文件,那么 get_alternative_name() 将会被调用来得到另一个替代的名称。

使用你的自定义存储引擎

New in Django 4.2.

The first step to using your custom storage with Django is to tell Django about the file storage backend you'll be using. This is done using the STORAGES setting. This setting maps storage aliases, which are a way to refer to a specific storage throughout Django, to a dictionary of settings for that specific storage backend. The settings in the inner dictionaries are described fully in the STORAGES documentation.

Storages are then accessed by alias from from the django.core.files.storage.storages dictionary:

from django.core.files.storage import storages

example_storage = storages["example"]