Django文件存储 默认存储系统解析


Posted in Python onAugust 02, 2019

Django默认使用的文件存储系统'django.core.files.storage.FileSystemStorage'是一个本地存储系统,由settings中的DEFAULT_FILE_STORAGE值确定。

class FileSystemStorage(location=None, base_url=None, file_permissions_mode=None, directory_permissions_mode=None)

FileSystemStorage类继承自Storage类,location是存储文件的绝对路径,默认值是settings中的MEDIA_ROOT值,base_url默认值是settings中的MEDIA_URL值。

当定义location参数时,可以无视MEDIA_ROOT值来存储文件:

from django.db import models
from django.core.files.storage import FileSystemStorage 
fs = FileSystemStorage(location='/media/photos') 
class Car(models.Model):
  ...
  photo = models.ImageField(storage=fs)

这样文件会存储在/media/photos文件夹。

可以直接使用Django的文件存储系统来存储文件:

>>> from django.core.files.storage import default_storage
>>> from django.core.files.base import ContentFile
 
>>> path = default_storage.save('/path/to/file', ContentFile('new content'))
>>> path
'/path/to/file'
 
>>> default_storage.size(path)
11
>>> default_storage.open(path).read()
'new content'
 
>>> default_storage.delete(path)
>>> default_storage.exists(path)
False

可以从FileSystemStorage类的_save方法看下上传文件是怎么存储的:

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:
      if self.directory_permissions_mode is not None:
        # os.makedirs applies the global umask, so we reset it,
        # for consistency with file_permissions_mode behavior.
        old_umask = os.umask(0)
        try:
          os.makedirs(directory, self.directory_permissions_mode)
        finally:
          os.umask(old_umask)
      else:
        os.makedirs(directory)
    except OSError as 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)
 
      # 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.
        flags = (os.O_WRONLY | os.O_CREAT | os.O_EXCL |
             getattr(os, 'O_BINARY', 0))
        # The current umask value is masked out by os.open!
        fd = os.open(full_path, flags, 0o666)
        _file = None
        try:
          locks.lock(fd, locks.LOCK_EX)
          for chunk in content.chunks():
            if _file is None:
              mode = 'wb' if isinstance(chunk, bytes) else 'wt'
              _file = os.fdopen(fd, mode)
            _file.write(chunk)
        finally:
          locks.unlock(fd)
          if _file is not None:
            _file.close()
          else:
            os.close(fd)
    except OSError as 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 self.file_permissions_mode is not None:
    os.chmod(full_path, self.file_permissions_mode)
 
  # Store filenames with forward slashes, even on Windows.
  return force_text(name.replace('\\', '/'))

方法中可以看出,先判断文件存储的目录是否存在,如果不存在,使用os.mkdirs()依次创建目录。

根据directory_permissions_mode参数来确定创建的目录的权限,应该为(0777 &~umask)。

然后使用os.open()创建文件,flags参数为(os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0)),

这样当文件已存在时,则报EEXIST异常,使用get_available_name()方法重新确定文件的名字。

mode为0o666,权限为(0666 &~umask)。

content为FILE对象,如一切正常,使用FILE.chunks()依次将内容写入文件。

最后,根据file_permissions_mode参数,修改创建文件的权限。

Python 相关文章推荐
Python获取网页上图片下载地址的方法
Mar 11 Python
python里使用正则表达式的组嵌套实例详解
Oct 24 Python
python3学习之Splash的安装与实例教程
Jul 09 Python
Python爬虫文件下载图文教程
Dec 23 Python
使用python制作一个为hex文件增加版本号的脚本实例
Jun 12 Python
Python 实现遥感影像波段组合的示例代码
Aug 04 Python
python中的逆序遍历实例
Dec 25 Python
你需要学会的8个Python列表技巧
Jun 24 Python
如何真正的了解python装饰器
Aug 14 Python
python使用建议与技巧分享(一)
Aug 17 Python
常用的Python代码调试工具总结
Jun 23 Python
分享Python异步爬取知乎热榜
Apr 12 Python
Django 迁移、操作数据库的方法
Aug 02 #Python
Django用户认证系统 组与权限解析
Aug 02 #Python
python3中eval函数用法使用简介
Aug 02 #Python
Django用户认证系统 Web请求中的认证解析
Aug 02 #Python
Django用户认证系统 User对象解析
Aug 02 #Python
浅谈python3中input输入的使用
Aug 02 #Python
Pycharm连接远程服务器并实现远程调试的实现
Aug 02 #Python
You might like
php通过COM类调用组件的实现代码
2012/01/11 PHP
PHP查询快递信息的方法
2015/03/07 PHP
PHP检测链接是否存在的代码实例分享
2016/05/06 PHP
php处理多图上传压缩代码功能
2018/06/13 PHP
一段非常简单的让图片自动切换js代码
2006/11/10 Javascript
jquery如何改变html标签的样式(两种实现方法)
2013/01/16 Javascript
nullJavascript中创建对象的五种方法实例
2013/05/07 Javascript
文本框中禁止非数字字符输入比如手机号码、邮编
2013/08/19 Javascript
Js 去掉字符串中的空格(实现代码)
2013/11/19 Javascript
JavaScript验证图片类型(扩展名)的函数分享
2014/05/05 Javascript
jquery实现人性化的有选择性禁用鼠标右键
2014/06/30 Javascript
JS实现鼠标滑过显示边框的菜单效果
2016/09/21 Javascript
js实现图片左右滚动效果
2017/02/27 Javascript
js中时间格式化的几种方法
2018/07/22 Javascript
浅谈让你的代码更简短,更整洁,更易读的ES6小技巧
2018/10/25 Javascript
Node.js 多线程完全指南总结
2019/03/27 Javascript
解决layui数据表格Date日期格式的回显Object的问题
2019/09/19 Javascript
Vue过渡效果之CSS过渡详解(结合transition,animation,animate.css)
2020/02/05 Javascript
JavaScript实现放大镜效果代码示例
2020/04/29 Javascript
Vue实现菜单切换功能
2020/11/08 Javascript
python动态加载变量示例分享
2014/02/17 Python
对于Python的Django框架使用的一些实用建议
2015/04/03 Python
python使用pil生成图片验证码的方法
2015/05/08 Python
python实现列表中由数值查到索引的方法
2018/06/27 Python
Python+Pandas 获取数据库并加入DataFrame的实例
2018/07/25 Python
Python中矩阵创建和矩阵运算方法
2018/08/04 Python
在pytorch中对非叶节点的变量计算梯度实例
2020/01/10 Python
python字符串拼接+和join的区别详解
2020/12/03 Python
企业年会主持词
2014/03/27 职场文书
劳资协议书范本
2014/04/23 职场文书
竞选班干部演讲稿
2014/04/24 职场文书
培训研修方案
2014/06/06 职场文书
乔迁之喜答谢词
2015/01/05 职场文书
鲁迅故里导游词
2015/02/05 职场文书
小学五年级(说明文3篇)
2019/08/13 职场文书
vue使用wavesurfer.js解决音频可视化播放问题
2022/04/04 Vue.js