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入门教程
Apr 03 Python
Python遍历numpy数组的实例
Apr 04 Python
Python3.6笔记之将程序运行结果输出到文件的方法
Apr 22 Python
Python格式化日期时间操作示例
Jun 28 Python
python中sys.argv函数精简概括
Jul 08 Python
Python基于多线程实现ping扫描功能示例
Jul 23 Python
django中的图片验证码功能
Sep 18 Python
python 3.74 运行import numpy as np 报错lib\site-packages\numpy\__init__.py
Oct 06 Python
python名片管理系统开发
Jun 18 Python
pandas使用函数批量处理数据(map、apply、applymap)
Nov 27 Python
在Python中实现字典反转案例
Dec 05 Python
Python源码解析之List
May 21 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
解析thinkphp中的M()与D()方法的区别
2013/06/22 PHP
基于PHP magic_quotes_gpc的使用方法详解
2013/06/24 PHP
PHP系统命令函数使用分析
2013/07/05 PHP
PHP面向对象教程之自定义类
2014/06/10 PHP
php实现用于计算执行时间的类实例
2015/04/18 PHP
PHP获取指定日期是星期几的实现方法
2016/11/30 PHP
PHP简单读取xml文件的方法示例
2017/04/20 PHP
JavaScript Array Flatten 与递归使用介绍
2011/10/30 Javascript
jquery获取焦点和失去焦点事件代码
2013/04/21 Javascript
jQuery控制的不同方向的滑动(向左、向右滑动等)
2014/07/18 Javascript
jQuery中on()方法用法实例详解
2015/02/06 Javascript
将JavaScript的jQuery库中表单转化为JSON对象的方法
2015/11/17 Javascript
异步加载JS、CSS代码(推荐)
2016/06/15 Javascript
gulp-uglify 与gulp.watch()配合使用时报错(重复压缩问题)
2016/08/24 Javascript
基于JavaScript实现随机颜色输入框
2016/12/10 Javascript
详解nodejs微信公众号开发——3.封装消息响应模块
2017/04/10 NodeJs
iview在vue-cli3如何按需加载的方法
2018/10/31 Javascript
JS实现盒子跟着鼠标移动及键盘方向键控制盒子移动效果示例
2019/01/29 Javascript
JavaScript 性能提升之路(推荐)
2019/04/10 Javascript
解决layui的table插件无法多层级获取json数据的问题
2019/09/19 Javascript
JS实现的雪花飘落特效示例
2019/12/03 Javascript
简述Vue中容易被忽视的知识点
2019/12/09 Javascript
web.py中调用文件夹内模板的方法
2014/08/26 Python
Python 异常处理的实例详解
2017/09/11 Python
Atom的python插件和常用插件说明
2018/07/08 Python
python 地图经纬度转换、纠偏的实例代码
2018/08/06 Python
Python实现多级目录压缩与解压文件的方法
2018/09/01 Python
Python后台开发Django会话控制的实现
2019/04/15 Python
使用pyecharts1.7进行简单的可视化大全
2020/05/17 Python
利用Python实现某OA系统的自动定位功能
2020/05/27 Python
美国女孩洋娃娃店:American Girl
2017/10/24 全球购物
抗洪救灾先进集体事迹材料
2014/05/26 职场文书
学生偷窃检讨书
2014/09/25 职场文书
2016开学第一课心得体会
2016/01/23 职场文书
解决redis sentinel 频繁主备切换的问题
2021/04/12 Redis
Flutter集成高德地图并添加自定义Maker的实践
2022/04/07 Java/Android