# 編寫自定義存儲系統 #
如果你需要提供自定義文件存儲 – 一個普遍的例子是在某個遠程系統上儲存文件 – 你可以通過定義一個自定義的儲存類來實現。你需要遵循以下步驟:
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\. 你的儲存類必須是 可以析構的,所以它在遷移中的一個字段上使用的時候可以被序列化。只要你的字段擁有自己可以[序列化](http://python.usyiyi.cn/django/topics/migrations.html#migration-serializing)的參數,你就可以為它使用`django.utils.deconstruct.deconstructible`類裝飾器(這也是Django用在`FileSystemStorage`上的東西)。
默認情況下,下面的方法會拋出`NotImplementedError`異常,并且必須覆寫它們。
+ [Storage.delete()](http://python.usyiyi.cn/django/ref/files/storage.html#django.core.files.storage.Storage.delete)
+ [Storage.exists()](http://python.usyiyi.cn/django/ref/files/storage.html#django.core.files.storage.Storage.exists)
+ [Storage.listdir()](http://python.usyiyi.cn/django/ref/files/storage.html#django.core.files.storage.Storage.listdir)
+ [Storage.size()](http://python.usyiyi.cn/django/ref/files/storage.html#django.core.files.storage.Storage.size)
+ [Storage.url()](http://python.usyiyi.cn/django/ref/files/storage.html#django.core.files.storage.Storage.url)
然而要注意,并不是這些方法全部都需要,可以故意省略一些。可以不必實現每個方法而仍然能擁有一個可以工作的儲存類。
比如,如果在特定的儲存后端中,列出內容的開銷比較大,你可以決定不實現`Storage.listdir`。
另一個例子是只處理寫入文件的后端。這種情況下,你不需要實現上面的任意一種方法。
根本上來說,需要實現哪種方法取決于你。如果不去實現一些方法,你會得到一個不完整(可能是不能用的)的接口。
你也會經常想要使用特意為自定義儲存對象設計的鉤子。它們是:
`_open(name, mode='rb')`
必需的。
被`Storage.open()`調用,這是儲存類用于打開文件的實際工具。它必須返回`File`對象,在大多數情況下,你會想要返回一些子類,它們實現了后端儲存系統特定的邏輯。
`_save(name, content)`
被`Storage.save()`調用。`name`必須事先通過`get_valid_name()` 和 `get_available_name()`過濾,并且`content`自己必須是一個`File`對象。
應該返回被保存文件的真實名稱(通常是傳進來的`name`,但是如果儲存需要修改文件名稱,則返回新的名稱來代替)。
`get_valid_name(name)`
返回適用于當前儲存系統的文件名。傳遞給該方法的`name`參數是發送給服務器的原始文件名稱,并移除了所有目錄信息。你可以覆寫這個方法,來自定義非標準的字符將會如何轉換為安全的文件名稱。
`Storage`提供的代碼只會保留原始文件名中的數字和字母字符、英文句號和下劃線,并移除其它字符。
`get_available_name(name, max_length=None)`
返回在儲存系統中可用的文件名稱,可能會顧及到提供的文件名稱。傳給這個方法的name參數需要事先過濾為儲存系統有效的文件名稱,根據上面描述的`get_valid_name()` 方法。
如果提供了`max_length`,文件名稱長度不會超過它。如果不能找到可用的、唯一的文件名稱,會拋出`SuspiciousFileOperation` 異常。
如果`name`命名的文件已存在,一個下劃線加上隨機7個數字或字母的字符串會添加到文件名稱的末尾,擴展名之前。
```
Changed in Django 1.7:
之前,下劃線和一位數字(比如"_1", "_2",以及其他)會添加到文件名稱的末尾,直到目標目錄中發現了可用的名稱。一些惡意的用戶會利用這一確定性的算法來進行dos攻擊。 這一變化也在1.6.6, 1.5.9, 和 1.4.14中出現。
```
```
Changed in Django 1.8:
新增了max_length參數。
```
[自定義儲存系統](http://python.usyiyi.cn/django/howto/custom-file-storage.html) 以相同方式工作:你可以把它們作為`storage`參數傳遞給`FileField`。
> 譯者:[Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html),原文:[Custom storage](https://docs.djangoproject.com/en/1.8/howto/custom-file-storage/)。
>
> 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 協議發布,轉載請保留作者署名和文章出處。
>
> [Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html)人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。
- 新手入門
- 從零開始
- 概覽
- 安裝
- 教程
- 第1部分:模型
- 第2部分:管理站點
- 第3部分:視圖和模板
- 第4部分:表單和通用視圖
- 第5部分:測試
- 第6部分:靜態文件
- 高級教程
- 如何編寫可重用的應用
- 為Django編寫首個補丁
- 模型層
- 模型
- 模型語法
- 元選項
- 模型類
- 查詢集
- 執行查詢
- 查找表達式
- 模型的實例
- 實例方法
- 訪問關聯對象
- 遷移
- 模式編輯器
- 編寫遷移
- 高級
- 管理器
- 原始的SQL查詢
- 聚合
- 多數據庫
- 自定義查找
- 條件表達式
- 數據庫函數
- 其它
- 遺留的數據庫
- 提供初始數據
- 優化數據庫訪問
- 視圖層
- 基礎
- URL配置
- 視圖函數
- 快捷函數
- 裝飾器
- 參考
- 內建的視圖
- TemplateResponse 對象
- 文件上傳
- 概覽
- File 對象
- 儲存API
- 管理文件
- 自定義存儲
- 基于類的視圖
- 概覽
- 內建顯示視圖
- 內建編輯視圖
- API參考
- 分類索引
- 高級
- 生成 CSV
- 生成 PDF
- 中間件
- 概覽
- 內建的中間件類
- 模板層
- 基礎
- 面向設計師
- 語言概覽
- 人性化
- 面向程序員
- 表單
- 基礎
- 概覽
- 表單API
- 內建的Widget
- 高級
- 整合媒體
- 開發過程
- 設置
- 概覽
- 應用程序
- 異常
- 概覽
- django-admin 和 manage.py
- 添加自定義的命令
- 測試
- 介紹
- 部署
- 概述
- WSGI服務器
- 部署靜態文件
- 通過email追蹤代碼錯誤
- Admin
- 管理操作
- 管理文檔生成器
- 安全
- 安全概述
- 說明Django中的安全問題
- 點擊劫持保護
- 加密簽名
- 國際化和本地化
- 概述
- 本地化WEB UI格式化輸入
- “本地特色”
- 常見的網站應用工具
- 認證
- 概覽
- 使用認證系統
- 密碼管理
- 日志
- 分頁
- 會話
- 數據驗證
- 其它核心功能
- 按需內容處理
- 重定向
- 信號
- 系統檢查框架