如果你需要提供一个自定义的文件存储-一个常见的例子便是在远程系统上存储文件-你可以通过定义一个自己的存储类来做这件事情,你将通过一下步骤:

  • 你自定义的存储系统一定是django.core.files.storage.Storage的子类
from django.core.files.storage import Storage

class MyStorage(Storage):
...
  • 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
  • 你的存储类必须实现_open()和_save()方法,以及其他的和你存储类相关的方法,下面会讲到
  • 另外,如果你的类提供本地文件存储,你必须覆盖path()方法,如果不,可以忽略这个方法

这是基类Storage的源码,我会在源码的注释中讲解一些内容一些要注意的内容

class Storage(object):
""" 存储基类,提供一些默认的行为供其他的存储系统继承或者覆盖(如果需要的话) """
# 下面的方法代表了私有方法的一个公共接口,除非绝对的需要,这些方法不应该被子类覆盖
def open(self, name, mode='rb'):
""" 从存储中检索特定的文件 """
return self._open(name, mode) def save(self, name, content):
""" 用给定的文件名保存给定的新内容,内容应该是一个合适的可以从头开始读取的File对象 """
# Get the proper name for the file, as it will actually be saved.
if name is None:
name = content.name name = self.get_available_name(name)
name = self._save(name, content) # Store filenames with forward slashes, even on Windows
return force_unicode(name.replace('\\', '/')) # 这些方法是一部分的公共API(已经实现好的,当然,你可以覆盖) def get_valid_name(self, name):
""" Returns a filename, based on the provided filename, that's suitable for
use in the target storage system. """
return get_valid_filename(name) def get_available_name(self, name):
""" Returns a filename that's free on the target storage system, and
available for new content to be written to. """
dir_name, file_name = os.path.split(name)
file_root, file_ext = os.path.splitext(file_name)
# If the filename already exists, add an underscore and a number (before
# the file extension, if one exists) to the filename until the generated
# filename doesn't exist.
count = itertools.count(1)
while self.exists(name):
# file_ext includes the dot.
name = os.path.join(dir_name, "%s_%s%s" % (file_root, count.next(), file_ext)) return name def path(self, name):
"""
Returns a local filesystem path where the file can be retrieved using
Python's built-in open() function. Storage systems that can't be
accessed using open() should *not* implement this method.
"""
raise NotImplementedError("This backend doesn't support absolute paths.") # 下面的这些方法是没有提供默认实现的公共API,子类一定要实现这些方法 def delete(self, name):
""" Deletes the specified file from the storage system. """
raise NotImplementedError() def exists(self, name):
""" Returns True if a file referened by the given name already exists in the
storage system, or False if the name is available for a new file. """
raise NotImplementedError() def listdir(self, path):
""" Lists the contents of the specified path, returning a 2-tuple of lists;
the first item being directories, the second item being files. """
raise NotImplementedError() def size(self, name):
""" Returns the total size, in bytes, of the file specified by name. """
raise NotImplementedError() def url(self, name):
""" Returns an absolute URL where the file's contents can be accessed
directly by a Web browser. """
raise NotImplementedError() def accessed_time(self, name):
""" Returns the last accessed time (as datetime object) of the file
specified by name. """
raise NotImplementedError() def created_time(self, name):
""" Returns the creation time (as datetime object) of the file
specified by name. """
raise NotImplementedError() def modified_time(self, name):
""" Returns the last modified time (as datetime object) of the file
specified by name. """
raise NotImplementedError()

看完这个源码,相信你已经知道该如何如写一个自己的存储系统类了(那些方法一定要有的,那些是可以直接用的,那些是一定要写的),下面我们看一下django自带的一个实现吧

class FileSystemStorage(Storage):
""" Standard filesystem storage """
def __init__(self, location=None, base_url=None):
if location is None:
location = settings.MEDIA_ROOT
self.base_location = location
self.location = abspathu(self.base_location)
if base_url is None:
base_url = settings.MEDIA_URL
self.base_url = base_url def _open(self, name, mode='rb'):
return File(open(self.path(name), mode)) def _save(self, name, content):
full_path = self.path(name) # Create any intermediate directories that do not exist.
# Note that there is a race between os.path.exists and os.makedirs:
# if os.makedirs fails with EEXIST, the directory was created
# concurrently, and we can continue normally. Refs #16082.
directory = os.path.dirname(full_path)
if not os.path.exists(directory):
try:
os.makedirs(directory)
except OSError, e:
if e.errno != errno.EEXIST:
raise
if not os.path.isdir(directory):
raise IOError("%s exists and is not a directory." % directory) # There's a potential race condition between get_available_name and
# saving the file; it's possible that two threads might return the
# same name, at which point all sorts of fun happens. So we need to
# try to create the file, but if it already exists we have to go back
# to get_available_name() and try again. while True:
try:
# This file has a file path that we can move.
if hasattr(content, 'temporary_file_path'):
file_move_safe(content.temporary_file_path(), full_path)
content.close() # This is a normal uploadedfile that we can stream.
else:
# This fun binary flag incantation makes os.open throw an
# OSError if the file already exists before we open it.
fd = os.open(full_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0))
try:
locks.lock(fd, locks.LOCK_EX)
for chunk in content.chunks():
os.write(fd, chunk)
finally:
locks.unlock(fd)
os.close(fd)
except OSError, e:
if e.errno == errno.EEXIST:
# Ooops, the file exists. We need a new file name.
name = self.get_available_name(name)
full_path = self.path(name)
else:
raise
else:
# OK, the file save worked. Break out of the loop.
break if settings.FILE_UPLOAD_PERMISSIONS is not None:
os.chmod(full_path, settings.FILE_UPLOAD_PERMISSIONS) return name def delete(self, name):
name = self.path(name)
# If the file exists, delete it from the filesystem.
# Note that there is a race between os.path.exists and os.remove:
# if os.remove fails with ENOENT, the file was removed
# concurrently, and we can continue normally.
if os.path.exists(name):
try:
os.remove(name)
except OSError, e:
if e.errno != errno.ENOENT:
raise def exists(self, name):
return os.path.exists(self.path(name)) def listdir(self, path):
path = self.path(path)
directories, files = [], []
for entry in os.listdir(path):
if os.path.isdir(os.path.join(path, entry)):
directories.append(entry)
else:
files.append(entry)
return directories, files def path(self, name):
try:
path = safe_join(self.location, name)
except ValueError:
raise SuspiciousOperation("Attempted access to '%s' denied." % name)
return os.path.normpath(path) def size(self, name):
return os.path.getsize(self.path(name)) def url(self, name):
if self.base_url is None:
raise ValueError("This file is not accessible via a URL.")
return urlparse.urljoin(self.base_url, filepath_to_uri(name)) def accessed_time(self, name):
return datetime.fromtimestamp(os.path.getatime(self.path(name))) def created_time(self, name):
return datetime.fromtimestamp(os.path.getctime(self.path(name))) def modified_time(self, name):
return datetime.fromtimestamp(os.path.getmtime(self.path(name)))

django “如何”系列5:如何编写自定义存储系统的更多相关文章

  1. Django学习系列4:编写第一个简单的应用代码

    首页视图编写 lists/tests.py from django.test import TestCasefrom django.urls import resolvefrom lists.view ...

  2. Win系列:VC++编写自定义组件

    在Visual Studio 中新建一个Visual C++的 Windows应用商店的Windows运行时组件项目,并将项目命名为FilePickerComponent.然后在项目的解决方案资源管理 ...

  3. django “如何”系列4:如何编写自定义模板标签和过滤器

    django的模板系统自带了一系列的内建标签和过滤器,一般情况下可以满足你的要求,如果觉得需更精准的模板标签或者过滤器,你可以自己编写模板标签和过滤器,然后使用{% load %}标签使用他们. 代码 ...

  4. Django 编写自定义的 404 / 500 报错界面

    Django 编写自定义的 404 / 500 报错界面 1. 首先 setting.py 文件中的 debug 参数设置成 false ,不启用调试. DEBUG = False 2. 在 temp ...

  5. Django编写自定义manage.py 命令

    官网文档地址:编写自定义 django-admin 命令 金句: 你所浪费的今天,正是昨天死的人所期待的明天. 开篇话: python manage.py <command> 的命令我们用 ...

  6. Wix打包系列(三)自定义Action(Custom Action)

    原文:Wix打包系列(三)自定义Action(Custom Action) 3.1 关于Action 我们已经知道如何生成具有标准安装界面的安装程序了,Windows Installer按照我们的界面 ...

  7. 《C#微信开发系列(2)-自定义菜单管理》

    2.0自定义菜单管理 ①接口说明 微信服务号聊天窗口下面的菜单项(有的公众号有启用有的则没有),这个可以在编辑模式简单配置,也可以在开发模式代码配置.微信公众平台开发者文档:微信公众号开发平台创建自定 ...

  8. python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API

    python  Django教程  之 模型(数据库).自定义Field.数据表更改.QuerySet API 一.Django 模型(数据库) Django 模型是与数据库相关的,与数据库相关的代码 ...

  9. 用mel编写自定义节点的属性编辑器界面

    用mel编写自定义节点的属性编辑器界面比较麻烦,而且网上例子又少,下面给出一个范例,说明基本的格式 // 初始化节点时调用 global proc initControl(string $attrNa ...

随机推荐

  1. 【BZOJ3240】【NOI2013】矩阵游戏(数论)

    [BZOJ3240][NOI2013]矩阵游戏(数论) 题面 BZOJ 题解 搞什么矩阵十进制快速幂加卡常? 直接数学推导不好吗? 首先观察如何从每一行的第一个推到最后一个 \(f[i]=a·f[i- ...

  2. BZOJ1833:[ZJOI2010]数字计数——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=1833 https://www.luogu.org/problemnew/show/P2602 给定两 ...

  3. BZOJ3123:[SDOI2013]森林——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=3123 https://www.luogu.org/problemnew/show/P3302 树上主 ...

  4. 20165218 2017-2018-1《Java程序设计》第二周学习总结

    20165218 2017-2018-1 <Java程序设计>第2周学习总结 教材学习内容总结 Ch2 基本数据类型与数组 Unicode字符集之中所有都叫做"字母", ...

  5. POJ.2387 Til the Cows Come Home (SPFA)

    POJ.2387 Til the Cows Come Home (SPFA) 题意分析 首先给出T和N,T代表边的数量,N代表图中点的数量 图中边是双向边,并不清楚是否有重边,我按有重边写的. 直接跑 ...

  6. HDOJ.1263 水果(map)

    水果 点我跳转到题面 点我一起学习STL-MAP 题意分析 给出多组测试数据,每组数据有多条信息.分别是水果种类,地点,和水果数目.每组信息要按照样例输出,并且输出要按照地点->水果种类的字典序 ...

  7. UVA10600:ACM Contest and Blackout(次小生成树)

    ACM Contest and Blackout 题目链接:https://vjudge.net/problem/UVA-10600 Description: In order to prepare ...

  8. js script type 部分属性值分析

    1. text/javascript: (1)<script type="text/javascript" src="Js/jquery-1.10.2.min.js ...

  9. Idrac6 to manage dell server

    最近idrac6挂了,java已经升级了 1.安装firefox浏览器,只有火狐是支持idrac最好的 2.安装JDK 3.配置configure java, 4.添加security,edit si ...

  10. springboot-为内置tomcat设置虚拟目录

    需求 项目使用springboot开发,以jar包方式部署.项目中文件上传均保存到D判断下的upload目录下. 在浏览器中输入http://localhost:8080/upload/logo_1. ...