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引用(import)文件夹下的py文件的方法
Aug 26 Python
Python中的闭包详细介绍和实例
Nov 21 Python
简单掌握Python中glob模块查找文件路径的用法
Jul 05 Python
Python全局变量与局部变量区别及用法分析
Sep 03 Python
面向初学者的Python编辑器Mu
Oct 08 Python
Python面向对象程序设计OOP入门教程【类,实例,继承,重载等】
Jan 05 Python
python获取磁盘号下盘符步骤详解
Jun 19 Python
Python爬取微信小程序通用方法代码实例详解
Sep 29 Python
python ssh 执行shell命令的示例
Sep 29 Python
利用python实现汉诺塔游戏
Mar 01 Python
Python 求向量的余弦值操作
Mar 04 Python
教你利用Selenium+python自动化来解决pip使用异常
May 20 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
基于文本的搜索
2006/10/09 PHP
Discuz7.2版的faq.php SQL注入漏洞分析
2014/08/06 PHP
thinkPHP中分页用法实例分析
2015/12/26 PHP
ThinkPHP路由机制简介
2016/03/23 PHP
PHP中in_array函数使用的问题与解决办法
2016/09/11 PHP
json格式化/压缩工具 Chrome插件扩展版
2010/05/25 Javascript
通过Javascript创建一个选择文件的对话框代码
2012/06/16 Javascript
js操作iframe兼容各种主流浏览器示例代码
2013/07/22 Javascript
js如何判断用户是否是用微信浏览器
2014/06/05 Javascript
jQuery实现在textarea指定位置插入字符或表情的方法
2015/03/11 Javascript
浅谈JavaScript的全局变量与局部变量
2016/06/10 Javascript
简易的JS计算器实现代码
2016/10/18 Javascript
Angularjs中的页面访问权限怎么设置
2016/11/11 Javascript
js 动态生成json对象、时时更新json对象的方法
2016/12/02 Javascript
JavaScript输入框字数实时统计更新
2017/06/17 Javascript
老生常谈js数据类型
2017/08/03 Javascript
对于input 框限定输入值为浮点型的js代码
2017/09/25 Javascript
vue cli构建的项目中请求代理与项目打包问题
2018/02/26 Javascript
Laravel admin实现消息提醒、播放音频功能
2019/07/10 Javascript
Python Tkinter基础控件用法
2014/09/03 Python
Python中if __name__ == "__main__"详细解释
2014/10/21 Python
Python PyQt4实现QQ抽屉效果
2018/04/20 Python
PyQt 实现使窗口中的元素跟随窗口大小的变化而变化
2019/06/18 Python
python切片的步进、添加、连接简单操作示例
2019/07/11 Python
python中的数组赋值与拷贝的区别详解
2019/11/26 Python
python中sort sorted reverse reversed函数的区别说明
2020/05/11 Python
Python基于opencv的简单图像轮廓形状识别(全网最简单最少代码)
2021/01/28 Python
天游软件面试
2013/11/23 面试题
初任培训自我鉴定
2013/10/07 职场文书
四风问题自查报告剖析材料
2014/02/08 职场文书
数学教研活动总结
2014/07/02 职场文书
2014年初级职称工作总结
2014/12/08 职场文书
工作检讨书范文
2015/01/23 职场文书
工程部主管岗位职责
2015/02/12 职场文书
2015夏季作息时间调整通知
2015/04/24 职场文书
关于Python中进度条的六个实用技巧分享
2022/04/05 Python