{"title":"如何编写一个自定义的文件存储类","version":"4.0","locale":"zh-hans","docname":"howto/custom-file-storage","url":"/zh-hans/4.0/howto/custom-file-storage/","canonical":"https://djangodocs.dev/zh-hans/4.0/howto/custom-file-storage/","summary":"如果你需要提供自定义文件储存功能——一个普通的例子是，把文件储存在远程系统中——自定义一个存储类可以完成这一任务来完成。下面是需要完成的具体步骤： 你自定义的存储系统必须为 Django.core.files.storage.Storage 的一个子类: Code Copy from…","html":"<h1>如何编写一个自定义的文件存储类<a class=\"heading-anchor\" href=\"#how-to-write-a-custom-storage-class\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>如果你需要提供自定义文件储存功能——一个普通的例子是，把文件储存在远程系统中——自定义一个存储类可以完成这一任务来完成。下面是需要完成的具体步骤：</p>\n<ol class=\"arabic\">\n<li><p>你自定义的存储系统必须为 <code class=\"docutils literal notranslate\"><span class=\"pre\">Django.core.files.storage.Storage</span></code> 的一个子类:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.files.storage</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Storage</span>\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MyStorage</span><span class=\"p\">(</span><span class=\"n\">Storage</span><span class=\"p\">):</span>\n    <span class=\"o\">...</span>\n</code></pre></div>\n</li>\n<li><p>Django 必须能以无参数实例化你的存储系统。意味着所有配置都应从 <code class=\"docutils literal notranslate\"><span class=\"pre\">django.conf.settings</span></code> 配置中获取:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.conf</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">settings</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.files.storage</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Storage</span>\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MyStorage</span><span class=\"p\">(</span><span class=\"n\">Storage</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">option</span><span class=\"o\">=</span><span class=\"kc\">None</span><span class=\"p\">):</span>\n        <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">option</span><span class=\"p\">:</span>\n            <span class=\"n\">option</span> <span class=\"o\">=</span> <span class=\"n\">settings</span><span class=\"o\">.</span><span class=\"n\">CUSTOM_STORAGE_OPTIONS</span>\n        <span class=\"o\">...</span>\n</code></pre></div>\n</li>\n<li><p>在你的存储类中，除了其他自定义的方法外，还必须实现  <a class=\"reference internal\" href=\"#django.core.files.storage._open\" title=\"django.core.files.storage._open\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">_open()</span></code></a> 以及 <a class=\"reference internal\" href=\"#django.core.files.storage._save\" title=\"django.core.files.storage._save\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">_save()</span></code></a> 等其他适合你的存储类的方法。关于这些方法，详情请查看下面的信息。</p>\n<p>另外，如果你的类提供了本地文件存储，它必须重写  <code class=\"docutils literal notranslate\"><span class=\"pre\">path()</span></code> 方法。</p>\n</li>\n<li><p>您的存储类必须是 <a class=\"reference internal\" href=\"/zh-hans/4.0/topics/migrations/#custom-deconstruct-method\"><span class=\"std std-ref\">deconstructible</span></a>，以便在迁移中的字段上使用它时可以序列化。 只要你的字段有自己的参数 <a class=\"reference internal\" href=\"/zh-hans/4.0/topics/migrations/#migration-serializing\"><span class=\"std std-ref\">serializable</span></a>，你可以使用 <code class=\"docutils literal notranslate\"><span class=\"pre\">django.utils.deconstruct.deconstructible</span></code> 类装饰器（这是 Django 在 FileSystemStorage 上使用的）。</p></li>\n</ol>\n<p>默认情况下，下面的方法将引发一个 NotImplementedError 的错误，并且通常它们必须被重写。</p>\n<ul class=\"simple\">\n<li><p><a class=\"reference internal\" href=\"/zh-hans/4.0/ref/files/storage/#django.core.files.storage.Storage.delete\" title=\"django.core.files.storage.Storage.delete\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Storage.delete()</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/4.0/ref/files/storage/#django.core.files.storage.Storage.exists\" title=\"django.core.files.storage.Storage.exists\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Storage.exists()</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/4.0/ref/files/storage/#django.core.files.storage.Storage.listdir\" title=\"django.core.files.storage.Storage.listdir\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Storage.listdir()</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/4.0/ref/files/storage/#django.core.files.storage.Storage.size\" title=\"django.core.files.storage.Storage.size\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Storage.size()</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/zh-hans/4.0/ref/files/storage/#django.core.files.storage.Storage.url\" title=\"django.core.files.storage.Storage.url\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Storage.url()</span></code></a></p></li>\n</ul>\n<p>可是，记住并非所有这些方法都是需要的，并且可能故意被省略。正因为如此，让每个方法未实现并仍然拥有一个可用储存是可能的。</p>\n<p>举例来说，如果列出某些存储后端的内容被证明代价会很昂贵，那么您可以决定不实现 Storage.listdir() 方法。</p>\n<p>另一个例子是只处理写入文件的后端。在这种情况下，你不需要实现上述任何方法。</p>\n<p>最终，你决定实现这些方法中的哪一个。一些方法未实现结果会生成部分（可能会损坏的）接口。</p>\n<p>你可能也经常会用到专为自定义存储对象设计的钩子函数。他们是：</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.core.files.storage._open\">\n<span class=\"sig-name descname\"><span class=\"pre\">_open</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">name</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">mode</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">'rb'</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.core.files.storage._open\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p><strong>要求</strong>。</p>\n<p>它将被 <code class=\"docutils literal notranslate\"><span class=\"pre\">Storage.open()</span></code> 调用，前者才是存储类用来打开文件的真正机制，这个方法必须要返回一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">文件</span></code> 对象。尽管在大多数时候，你想要这个方法返回一个继承于特定逻辑的后台存储系统的子类。</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.core.files.storage._save\">\n<span class=\"sig-name descname\"><span class=\"pre\">_save</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">name</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">content</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.core.files.storage._save\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>被称为 <code class=\"docutils literal notranslate\"><span class=\"pre\">Storage.save()</span></code>。这个 <code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code> 会早已经历 <code class=\"docutils literal notranslate\"><span class=\"pre\">get_valid_name()</span></code> 和 <code class=\"docutils literal notranslate\"><span class=\"pre\">get_available_name()</span></code>，并且 <code class=\"docutils literal notranslate\"><span class=\"pre\">content</span></code> 将会成为 <code class=\"docutils literal notranslate\"><span class=\"pre\">File</span></code> 对象自身。</p>\n<p>应该返回保存的文件名的实际名称（通常是传入“name”，但如果内存需要改变文件名，则返回新名称）。</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.core.files.storage.get_valid_name\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_valid_name</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">name</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.core.files.storage.get_valid_name\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>返回适用于底层存储系统的文件名。 传递给此方法的 <code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code> 参数既不是发送给服务器的原始文件名，如果 <code class=\"docutils literal notranslate\"><span class=\"pre\">upload_to</span></code> 是可调用的，则在删除任何路径信息后由该方法返回的文件名。 重写此操作可以自定义如何将非标准字符转换为安全文件名。</p>\n<p>“Storage”上提供的代码仅保留原始文件名中的字母数字字符，句点和下划线，并删除其他所有内容。</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.core.files.storage.get_alternative_name\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_alternative_name</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">file_root</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">file_ext</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.core.files.storage.get_alternative_name\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>将会在 <code class=\"docutils literal notranslate\"><span class=\"pre\">file_root</span></code> 和 <code class=\"docutils literal notranslate\"><span class=\"pre\">file_ext</span></code> 这两个参数的基础上返回另一个文件名。默认情况下，在执行前一个下划线再加上 7 个由字母和数字组成的字符串将将会追加到原来的文件名后。</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.core.files.storage.get_available_name\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_available_name</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">name</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">max_length</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.core.files.storage.get_available_name\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>返回存储机制中可用的文件名，可能会考虑提供的文件名。 根据上述 <code class=\"docutils literal notranslate\"><span class=\"pre\">get_valid_name()</span></code> 方法，传递给此方法的 <code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code> 参数已经被清除为一个对存储系统有效的文件名。</p>\n<p>返回的文件名的长度不会超过 <code class=\"docutils literal notranslate\"><span class=\"pre\">max_length</span></code>，如果该参数被提供的话。若找不到一个可用的独一无二的文件名，则抛出一个 <a class=\"reference internal\" href=\"/zh-hans/4.0/ref/exceptions/#django.core.exceptions.SuspiciousOperation\" title=\"django.core.exceptions.SuspiciousOperation\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">SuspiciousFileOperation</span></code></a> 异常。</p>\n<p>假如已经存在名为 <code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code> 的文件，那么 <code class=\"docutils literal notranslate\"><span class=\"pre\">get_alternative_name()</span></code> 将会被调用来得到另一个替代的名称。</p>","rootId":"how-to-write-a-custom-storage-class","toc":[],"breadcrumbs":[{"docname":"howto/index","title":"操作指南","url":"/zh-hans/4.0/howto/"}],"prev":{"docname":"howto/custom-template-tags","title":"如何编写自定义的模板标签和过滤器","url":"/zh-hans/4.0/howto/custom-template-tags/"},"next":{"docname":"howto/deployment/index","title":"如何部署 Django","url":"/zh-hans/4.0/howto/deployment/"},"formats":{"html":"/zh-hans/4.0/howto/custom-file-storage/","markdown":"/zh-hans/4.0/howto/custom-file-storage.md","json":"/zh-hans/4.0/howto/custom-file-storage.json"},"source":"https://github.com/django/django/blob/stable/4.0.x/docs/howto/custom-file-storage.txt","official":"https://docs.djangoproject.com/zh-hans/4.0/howto/custom-file-storage/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0"],"inLocales":["en","zh-hans","fr","ja","id","it","pt-br","ko","es","el","pl"]}