Django 图片等文件上传下载 之 文件存储系统 开发环境 生产环境

django 静态文件

1.静态文件开发环境配置

项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理。在html页面中调用时,也需要指定静态文件的路径,Django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。
为了提供静态文件,需要配置两个参数:

  • 先创建目录: static_files

  • 在setting.py文件中配置:

    • STATICFILES_DIRS 存放查找静态文件的目录
    • STATIC_URL 访问静态文件的URL前缀
  • 示例:

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static_files'),
]
  • 说明: 此时在static_files添加的任何静态文件都可以使用网址 /static/文件在static_files中的路径 来访问了
  • 注意:
    • Django 仅在调试模式下(DEBUG=True)能对外提供静态文件。
    • 当DEBUG=False工作在生产模式时,Django不再对外提供静态文件,需要是用collectstatic命令来收集静态文件并交由其他静态文件服务器来提供。
2.静态文件生产环境部署

静态文件除了我们业务之外,django本身还有自己的静态文件,如果rest_framework、admin等。我们需要收集这些静态文件,集中一起放到静态文件服务器中。

  • 先创建目录 static
  • 在setting.py文件中配置:
    • STATIC_ROOT = os.path.join(os.path.dirname(os.path.dirname(BASE_DIR)), 'front_end/static')
  • 执行收集命令: python manage.py collectstatic
  • 生产环境静态文件服务器配置(这里采用nginx)
    • 1.打开配置文件,一般是 sudo vim /usr/local/nginx/conf/nginx.conf
    • 2.```
    • server {
    •   	listen       80; 
        	
          server_name  www.meiduo.site;
           
         location / {
              root   /home/python/Desktop/front_end_pc;
              index  index.html index.htm;
             }
      
      // 余下省略(文档原因,自己对齐)
      }```
    • 3.重启 nginx: sudo /usr/local/nginx/sbin/nginx -s reload

django 自带文件管理系统

1.一些特点:

1.默认情况下,Django会将上传的图片保存在本地服务器上,需要配置保存的路径。
2.一般项目本身和业务结合较多的文件, 会配置一个 MEDIA_ROOT参数结合 BASE_DIR 来形成一个完整的路径,单独放置在 static 文件目录下;
3.django 会在global_settins中, 默认: MEDIA_ROOT = ‘’
4.常见在 ImageField 字段中 upload_to 相关联

2.自定义django的文件存储系统(类)

  • 1.继承: django.core.files.storage.Storage
  • 2.Storage 继承 object类, 而且没有 __init__.py, 所以实例化的时候不需要参数,也就是说任何配置都应该从django.conf.settings中获取
from django.conf import settings
from django.core.files.storage import Storage

class MyStorage(Storage):
    def __init__(self, base_url=None, client_conf=None):
        if base_url is None:
            base_url = settings.File_SERVER_URL
        self.base_url = base_url
        if client_conf is None:
            client_conf = settings.File_CLIENT_CONF
        self.client_conf = client_conf
  • 3.存储类中必须实现_open()和_save()方法,以及任何后续使用中可能用到的其他方法。

  • _open(name, mode=‘rb’)
    被Storage.open()调用,在打开文件时被使用。

  • _save(name, content)
    被Storage.save()调用,name是传入的文件名,content是Django接收到的文件内容,该方法需要将content文件内容保存。
    Django会将该方法的返回值保存到数据库中对应的文件字段,也就是说该方法应该返回要保存在数据库中的文件名称信息。

  • exists(name)
    如果名为name的文件在文件系统中存在,则返回True,否则返回False。

  • url(name)
    返回文件的完整访问URL

  • delete(name)
    删除name的文件

  • listdir(path)
    列出指定路径的内容

  • size(name)
    返回name文件的总大小
    注意,并不是这些方法全部都要实现,可以省略用不到的方法。

  • 4.需要为存储类添加django.utils.deconstruct.deconstructible装饰器

3.配置 自定义的文件存储类

  • 1.django 默认在global_settings中配置了: DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
  • 2.我们这里需要 自己配置 DEFAULT_FILE_STORAGE
  • 3.如果是保存到服务器,我们可能还需要配置一些 url port path 等文件存储服务器信息.

4.其他静态文件服务器:

  • FastDFS
  • OSS

5.自定义可以参考源码 FileSystemStorage 标准实现方法

@deconstructible
class FileSystemStorage(Storage):
    """
    Standard filesystem storage
    """
    # The combination of O_CREAT and O_EXCL makes os.open() raise OSError if
    # the file already exists before it's opened.
    OS_OPEN_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0)

    def __init__(self, location=None, base_url=None, file_permissions_mode=None,
                 directory_permissions_mode=None):
        self._location = location
        self._base_url = base_url
        self._file_permissions_mode = file_permissions_mode
        self._directory_permissions_mode = directory_permissions_mode
        setting_changed.connect(self._clear_cached_properties)

    def _clear_cached_properties(self, setting, **kwargs):
        """Reset setting based property values."""def _value_or_setting(self, value, setting):
        return setting if value is None else value

    @cached_property
    def base_location(self):
        return self._value_or_setting(self._location, settings.MEDIA_ROOT)

    @cached_property
    def location(self):
        return os.path.abspath(self.base_location)

    @cached_property
    def base_url(self):
        if self._base_url is not None and not self._base_url.endswith('/'):
            self._base_url += '/'
        return self._value_or_setting(self._base_url, settings.MEDIA_URL)

    @cached_property
    def file_permissions_mode(self):
        return self._value_or_setting(self._file_permissions_mode, settings.FILE_UPLOAD_PERMISSIONS)

    @cached_property
    def directory_permissions_mode(self):
        return self._value_or_setting(self._directory_permissions_mode, settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS)

    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.
        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 FileExistsError:
                # There's a race between os.path.exists() and os.makedirs().
                # If os.makedirs() fails with FileExistsError, the directory
                # was created concurrently.
                pass
        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:
                    # The current umask value is masked out by os.open!
                    fd = os.open(full_path, self.OS_OPEN_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 FileExistsError:
                # A new name is needed if the file exists.
                name = self.get_available_name(name)
                full_path = self.path(name)
            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 name.replace('\\', '/')

    def delete(self, name):pass

    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.scandir(path):
            if entry.is_dir():
                directories.append(entry.name)
            else:
                files.append(entry.name)
        return directories, files

    def path(self, name):
        return safe_join(self.location, name)

    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.")
        url = filepath_to_uri(name)
        if url is not None:
            url = url.lstrip('/')
        return urljoin(self.base_url, url)

    def _datetime_from_timestamp(self, ts):
        """
        If timezone support is enabled, make an aware datetime object in UTC;
        otherwise make a naive one in the local timezone.
        """def get_accessed_time(self, name):
        return self._datetime_from_timestamp(os.path.getatime(self.path(name)))

    def get_created_time(self, name):
        return self._datetime_from_timestamp(os.path.getctime(self.path(name)))

    def get_modified_time(self, name):
        return self._datetime_from_timestamp(os.path.getmtime(self.path(name)))

6.还可以修改 upload_to = “” , 来制定文件保存的位置. 更复杂的还可以通过 lambda函数 , 自定义函数 等方式来实现 将文件文件保存在指定的位置, 甚至是动态的位置.

7.django 在 FileField 类中通过 self.storage = storage or default_storage 实现 default_storage 指定默认文件存储系统的功能的.

参考下面的源码…

class FileField(Field):

    # The class to wrap instance attributes in. Accessing the file object off
    # the instance will always return an instance of attr_class.
    attr_class = FieldFile

    # The descriptor to use for accessing the attribute off of the class.
    descriptor_class = FileDescriptor

    description = _("File")

    def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
        self._primary_key_set_explicitly = 'primary_key' in kwargs

        self.storage = storage or default_storage  # 调用默认的 文件存储系统
        self.upload_to = upload_to

        kwargs.setdefault('max_length', 100)
        super().__init__(verbose_name, name, **kwargs)
    # 略
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值