dorian

导航

 

  fastDFS是由淘宝余庆开发的。开源,免费。主要用于大规模的文件存储。Django对文件的支持,默认是放在工程目录一起,如果文件量大,增加服务压力。所以,尽量把文件服务器分离开了,专门存储文件。

  本节主要介绍如何通过django的自定义存储,自动把文件存储到fastDFS上。

01 安装客户端插件

  fastDFS客户端插件,网上代码基本上都是下载fdfs_client-py-master.zip,而fdfs_client-py-master.zip不能在线安装,必须单独下载文件,而且,有些文件还安装要报错。因此,我们采用py3Fdfs插件,在工程创建时已安装了。 

02 下载fastDFS的client.conf配置文件。

  在fastDFS服务器上,下载client.conf文件,并保存在Configurations目录下。

03 重写Storage类

  Storage主要功能是上传文件,重写该类,把文件自动上传到fastDFS服务器。

  有两点需要说明:

  前端把文件上传到django服务器,如果保存之后再上传到fastDFS服务器,会导致效率低下,而fastDFS提供了缓存文件上传。也就是说,当前端把文件传到Django服务器,直接就上传到fastDFS服务器,不需要再存为文件。但此时,fastDFS返回的文件名没有扩展名,如果浏览器访问,浏览器不认识的文件,不会自动打开,而是直接下载。所以,我们必须要告诉fastDFS返回文件的扩展名。如图片,txt等文件,直接浏览器就可以打开。同时,处理文件扩展名的时候,特别要注意文件本身中有多个点,而扩展名只是最后一个点后面的名字。

  另外,fdfs_client-py-master.zip和fastDFS函数名都相同,但使用方式不同,这段存储代码来自于网上,但已经过修改。能正常运行。

  在GeneralTools目录下创建文件FastDFSStorage.py文件,内容如下:

from django.conf import settings
from django.core.files.storage import Storage
from django.utils.deconstruct import deconstructible
from fdfs_client.client import Fdfs_client, get_tracker_conf
import os


@deconstructible
class FastDFSStorage(Storage):
    def __init__(self, base_url=None, client_conf=None):
        """
        初始化
        :param base_url: 用于构造图片完整路径使用,图片服务器的域名
        :param client_conf: FastDFS客户端配置文件的路径
        """
        if base_url is None:
            base_url = settings.FDFS_URL
        self.base_url = base_url
        if client_conf is None:
            client_conf = settings.FDFS_CLIENT_CONF
        self.client_conf = client_conf
        self.tracker_obj = get_tracker_conf(self.client_conf)

    def _open(self, name, mode='rb'):
        """
        用不到打开文件,所以省略
        """
        pass

    def _save(self, name, content):
        """
        在FastDFS中保存文件
        :param name: 传入的文件名
        :param content: 文件内容
        :return: 保存到数据库中的FastDFS的文件名
        """
        client = Fdfs_client(self.tracker_obj)
        # 告诉fastDFS服务器,返回文件的扩展名。
        ext_name = os.path.splitext(name)[1][1:]
        ret = client.upload_by_buffer(content.read(), ext_name)
        if ret.get("Status") != "Upload successed.":
            raise Exception("upload file failed")
        file_name = bytes(ret.get("Remote file_id")).decode()
        # 必须替换路径中的分隔符,否则,查询不到上传的文件。
        return file_name

    def url(self, name):
        """
        返回文件的完整URL路径
        :param name: 数据库中保存的文件名
        :return: 完整的URL
        """
        if name.startswith('http'):
            return name
        else:
            return self.base_url + name

    def exists(self, name):
        """
        判断文件是否存在,FastDFS可以自行解决文件的重名问题
        所以此处返回False,告诉Django上传的都是新文件
        :param name:  文件名
        :return: False
        """
        return False

04 在settings.py中配置fastDFS

FDFS_SERVER = '49.235.156.156'
DEFAULT_FILE_STORAGE = 'GeneralTools.FastDFSStorage.FastDFSStorage'
# 自定义两个变量,分别表示client.conf文件的路径和fdfs的url
FDFS_CLIENT_CONF = os.path.join(BASE_DIR, 'Configurations', 'client.conf')
FDFS_URL = 'http://' + FDFS_SERVER + ':80/'

05 创建模型

  在该模型中定义一个图片字段。我们改一下之前在Organizations/models.py中创建的UserInfo模型。并执行数据迁移。

from django.db import models
from django.contrib.auth.models import AbstractUser

from GeneralTools.BaseModel import BaseModel


class UserInfo(AbstractUser, BaseModel):
    openid = models.CharField(max_length=30, unique=True, verbose_name='微信openID', help_text='微信openID')
    mobile = models.CharField(max_length=11, unique=True, verbose_name='手机号', help_text='手机号')
    # 默认的username是有唯一约束的,暂存入手机号。另增一个name字段存放姓名(微信昵称)
    name = models.CharField(max_length=30, null=True, blank=True, verbose_name='姓名', help_text='姓名')
    photo_url = models.ImageField(upload_to='user', null=True, blank=True, verbose_name='头像', help_text='头像')

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'UserInfo'
        verbose_name_plural = '001  用户信息表'

06 创建视图

  在Organizations/views下创建UserSave.py文件。

from rest_framework import serializers
import re
from rest_framework_jwt.settings import api_settings
from django_redis import get_redis_connection
import logging
from rest_framework.generics import CreateAPIView
from Applications.Organizations.models import UserInfo

# 获取在配置文件中定义的logger,用来记录日志
logger = logging.getLogger('Organizations')


class UserRegisterSer(serializers.ModelSerializer):
    """
    用户注册序列化器
    """

    class Meta:
        model = UserInfo
        fields = ('id', 'name', 'photo_url', 'password', 'mobile', 'openid')

    def create(self, validated_data):
        """
        用户注册,向数据库保存用户
        """

        # 移除数据库模型类中不存在的属性
        validated_data['username'] = validated_data['mobile']
        try:
            user = super().create(validated_data)
            # 调用django的认证系统加密密码
            user.set_password(validated_data['password'])
            user.save()
            return user
        except Exception as e:
            logger.error(str(e))
        return validated_data


class UserSave(CreateAPIView):
    """
    用户注册第三步:创建用户
    POST /OrgsAndUsers/user/register/create/
    """

    serializer_class = UserRegisterSer

07 运行工程

  在浏览器访问url,注:在接口文档中访问,不能选择文件。

08 查询数据库,我们看到的内容如下:

  我们看到,第一条记录的路径分隔符没改,后面的都改好了。

  但数据库中,只保存了相对路径,那么,查询的时候,是否需要自己手动去拼接完整路径呢?

  先看查询结果:

09 编写查询接口

  在Applications/Organizations/views/UserSave.py文件中,增加一个查询接口,并配置url,查看返回的数据。

class UserSaveList(ListAPIView):
    queryset = UserInfo.objects.all()
    serializer_class = UserRegisterSer

10 访问接口,看到以下效果

 

   我们看到,真是太神奇了,居然自动拼接成了完整路径。

 

posted on 2020-03-03 20:41  dorian  阅读(248)  评论(0编辑  收藏  举报