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封装shell命令实例分析
May 05 Python
Windows中安装使用Virtualenv来创建独立Python环境
May 31 Python
Python编程产生非均匀随机数的几种方法代码分享
Dec 13 Python
python 统计数组中元素出现次数并进行排序的实例
Jul 02 Python
Django上使用数据可视化利器Bokeh解析
Jul 31 Python
Django配置MySQL数据库的完整步骤
Sep 07 Python
python图像处理模块Pillow的学习详解
Oct 09 Python
python的等深分箱实例
Nov 22 Python
带你彻底搞懂python操作mysql数据库(cursor游标讲解)
Jan 06 Python
Django User 模块之 AbstractUser 扩展详解
Mar 11 Python
CentOS 7如何实现定时执行python脚本
Jun 24 Python
Python如何使用vars返回对象的属性列表
Oct 17 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 新手入门教程
2009/08/03 PHP
php iconv() : Detected an illegal character in input string
2010/12/05 PHP
基于magic_quotes_gpc与magic_quotes_runtime的区别与使用介绍
2013/04/22 PHP
关于尾递归的使用详解
2013/05/02 PHP
学习php设计模式 php实现桥梁模式(bridge)
2015/12/07 PHP
CI框架使用composer安装的依赖包步骤与方法分析
2016/11/21 PHP
浅谈PHP的排列组合(如输入a,b,c 输出他们的全部组合)
2017/03/14 PHP
laravel中短信发送验证码的实现方法
2018/04/25 PHP
Laravel框架模板加载,分配变量及简单路由功能示例
2018/06/11 PHP
PHP实现的权重算法示例【可用于游戏根据权限来随机物品】
2019/02/15 PHP
php自动加载代码实例详解
2021/02/26 PHP
jquery 圆形旋转图片滚动切换效果
2011/01/19 Javascript
在JavaScript中监听IME键盘输入事件
2011/05/29 Javascript
jquery遍历筛选数组的几种方法和遍历解析json对象
2013/12/13 Javascript
JS获取URL中参数值(QueryString)的4种方法分享
2014/04/12 Javascript
javascript 构造函数方式定义对象
2015/01/02 Javascript
Vue.js 表单校验插件
2016/08/14 Javascript
JS控制FileUpload的上传文件类型实例代码
2016/10/07 Javascript
详解Vue打包优化之code spliting
2018/04/09 Javascript
vue实现多个echarts根据屏幕大小变化而变化实例
2020/07/19 Javascript
解决iview table组件里的 固定列 表格不自适应的问题
2020/11/13 Javascript
微信小程序实现页面左右滑动
2020/11/16 Javascript
[01:15:15]VG VS EG Supermajor小组赛B组胜者组第一轮 BO3第二场 6.2
2018/06/03 DOTA
python中字典dict常用操作方法实例总结
2015/04/04 Python
简单谈谈Python中的几种常见的数据类型
2017/02/10 Python
Python使用grequests(gevent+requests)并发发送请求过程解析
2019/09/25 Python
python如何求圆的面积
2020/07/01 Python
Algenist奥杰尼官网:微藻抗衰老护肤品牌
2017/07/15 全球购物
eBay法国购物网站:eBay.fr
2017/10/21 全球购物
委托证明的格式
2014/01/10 职场文书
职称评定自我鉴定
2014/03/18 职场文书
《特殊的葬礼》教学反思
2014/04/27 职场文书
激励口号大全
2014/06/17 职场文书
国庆节标语大全
2014/10/08 职场文书
2014年销售助理工作总结
2014/12/01 职场文书
Nginx 根据URL带的参数转发的实现
2021/04/01 Servers