datetime:2020/5/14 16:17
author:nzb

Django-Fastdfs重写存储类

  • csnd文章

  • 源码解析

  • 本地存储类

  • 重写存储类

  • client.conf

          # connect timeout in seconds
          # default value is 30s
          connect_timeout=30
    
          # network timeout in seconds
          # default value is 30s
          network_timeout=60
    
          # the base path to store log files
          # base_path=C:\Users\Admin\PycharmProjects\alumnus_circle\venv
          # FastDFS客户端存放日志文件的目录
          base_path = /data/log/fastdfs/fastdfs.log
    
          # tracker_server can ocur more than once, and tracker_server format is
          #  "host:port", host can be hostname or ip address
          # 运行tracker服务的机器ip
          tracker_server = 172.26.6.129:22122
    
          #standard log level as syslog, case insensitive, value list:
          ### emerg for emergency
          ### alert
          ### crit for critical
          ### error
          ### warn for warning
          ### notice
          ### info
          ### debug
          log_level=info
    
          # if use connection pool
          # default value is false
          # since V4.05
          use_connection_pool = false
    
          # connections whose the idle time exceeds this time will be closed
          # unit: second
          # default value is 3600
          # since V4.05
          connection_pool_max_idle_time = 3600
    
          # if load FastDFS parameters from tracker server
          # since V4.05
          # default value is false
          load_fdfs_parameters_from_tracker=false
    
          # if use storage ID instead of IP address
          # same as tracker.conf
          # valid only when load_fdfs_parameters_from_tracker is false
          # default value is false
          # since V4.05
          use_storage_id = false
    
          # specify storage ids filename, can use relative or absolute path
          # same as tracker.conf
          # valid only when load_fdfs_parameters_from_tracker is false
          # since V4.05
          storage_ids_filename = storage_ids.conf
    
          #HTTP settings
          http.tracker_server_port=80
    
          #use "#include" directive to include HTTP other settiongs
          ##include http.conf
    
  • fdfs-storage.py

  # _*_ encoding:utf-8 _*_
  __author__ = 'nzb'
  __datetime__ = '2020/1/3 15:18'

  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


  @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

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

      def _save(self, name, content):
          """
          在FastDFS中保存文件
          :param name: 传入的文件名
          :param content: 文件内容
          :return: 保存到数据库中的FastDFS的文件名
          """
          client = Fdfs_client(self.client_conf)
          filename = content.name
          filename_ext = filename.split('.')
          if len(filename_ext) <= 1:
              filename_ext = ''  # 没有扩展名的情况
          else:
              filename_ext = filename_ext[-1]
          ret = client.upload_by_buffer(content.read(), file_ext_name=filename_ext)
          if ret.get("Status") != "Upload successed.":
              raise Exception("upload file failed")
          file_name = ret.get("Remote file_id")
          file_name = file_name.replace('\\', "/")
          return file_name

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

      def exists(self, name):
          """
          判断文件是否存在,FastDFS可以自行解决文件的重名问题
          所以此处返回False,告诉Django上传的都是新文件
          :param name:  文件名
          :return: False
          """
          return False
  • settings.py
  # FastDFS
  # django文件存储
  DEFAULT_FILE_STORAGE = 'utils.fastdfs.fdfs_storage.FastDFSStorage'
  FDFS_URL = 'http://img.example.com/'
  FDFS_CLIENT_CONF = os.path.join(BASE_DIR, 'utils/fastdfs/client.conf')

本地存储重命名

  • helper.py
# 文件重命名
@deconstructible
class RenameFile(object):

    def __init__(self, upload_to):

        self.upload_to = upload_to

    def __call__(self, instance, filename: str, *args, **kwargs) -> str:
        filename_ext = filename.split('.')
        if len(filename_ext) <= 1:
            filename_ext = ''  # 没有扩展名的情况
        else:
            filename_ext = filename_ext[-1]
        filename = '{name}.{ext}'.format(name=uuid.uuid4().hex[:16], ext=filename_ext)
        upload_to = posixpath.join(self.upload_to, instance.stu.sno,
                                   datetime.datetime.now().strftime(constants.UPLOAD_DIR_FORMAT))

        return posixpath.join(upload_to, filename)
  • models.py
class FeedBackFile(models.Model):
    file = models.FileField(verbose_name="意见反馈举报附件", upload_to=RenameFile("upload/feedback/"))
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")

    class Meta:
        verbose_name = "意见反馈附件"
        verbose_name_plural = verbose_name
  • 头部判断
import struct

# 常见文件格式的文件头
ALLOW_FILETYPE = {
    "FFD8FF": "JPEG (jpg)",
    "89504E47": "PNG (png)",
    "47494638": "GIF (gif)",
    "49492A00": "TIFF (tif)",
    "41433130": "CAD (dwg)",
    "D0CF11E0": "MS Word/Excel (xls.or.doc)",
    "255044462D312E": "Adobe Acrobat (pdf)",
    "504B0304": "ZIP Archive (zip)",
    "52617221": "RAR Archive (rar)",
    "41564920": "AVI (avi)"
}


# 字节码转16进制字符串
def bytes2hex(bytes):
    num = len(bytes)
    hexstr = u""
    for i in range(num):
        t = u"%x" % bytes[i]
        if len(t) % 2:
            hexstr += u"0"
        hexstr += t
    return hexstr.upper()


def validate_file(file):
    """
    根据文件头判断文件类型
    文件后缀不可信,并且后缀在linux系统下是没有这个概念的,所以通过文件中的头部标识来判断
    :param file:IO文件
    :return: xxx:文件类型,unknown:未知文件(不支持)
    """
    # binfile = open(file, 'rb')  # 必需二制字读取
    tl = ALLOW_FILETYPE
    ftype = 'unknown'
    for hcode in tl.keys():
        numOfBytes = len(hcode) / 2  # 需要读多少字节
        file.seek(0)  # 每次读取都要回到文件头,不然会一直往后读取
        # hbytes = struct.unpack_from("B" * numOfBytes, binfile.read(numOfBytes))  # 一个 "B"表示一个字节
        hbytes = struct.unpack_from("B" * int(numOfBytes), file.read(int(numOfBytes)))  # 一个 "B"表示一个字节
        f_hcode = bytes2hex(hbytes)
        if f_hcode == hcode:
            ftype = tl[hcode]
            break
    file.seek(0)  # 回到文件头
    return ftype


if __name__ == '__main__':
    pass
    ret = validate_file('./test.jpg')
    print(ret)

results matching ""

    No results matching ""