• 技术文章 >后端开发 >Python教程

    FastDFS加Redis实现自定义文件名存储海量文件

    高洛峰高洛峰2016-10-18 10:27:51原创1117
    FastDFS非常适合存储大量的小文件,遗憾的是本身不支持自定义文件名,文件名是存储成功以后根据存储位置生成的一个file_id。很多应用场景不得不使用自定义文件名,在不修改其源码的情况下,可以在存储客户端fdfs_client增加一个用来存储自定义文件名和fastdfs的file_id之间的映射关系的数据库间接实现自定义文件名的存取和访问,在这里我们选用了reids。顺便说一下,淘宝也有一个类似于FastDFS的文件存储系统TFS,对于自定义文件名,它是用mysql来存储映射关系的,我认为在高并发访问下mysql本身就是瓶颈,因此在这个方案中采用了redis。

    准备工作:

    fastdfs环境安装...略...(官方:https://code.google.com/p/fastdfs/)

    redis环境安装...略...(官方:http://redis.io/)

    用python实现,因此需要安装fastdfs的python客户端(下载:https://fastdfs.googlecode.com/files/fdfs_client-py-1.2.6.tar.gz)

    python的redis客户端,到https://pypi.python.org/pypi/redis下载

    # -*- coding: utf-8 -*-
    import setting
    from fdfs_client.client import *
    from fdfs_client.exceptions import *
      
    from fdfs_client.connection import *
      
    import redis
    import time
    import logging
    import random
      
    logging.basicConfig(format='[%(levelname)s]: %(message)s', level=logging.DEBUG)
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
      
      
    class RedisError(Exception):
         def __init__(self, value):
             self.value = value
         def __str__(self):
             return repr(self.value)
      
    class fastdfsClient(Fdfs_client):
        def __init__(self):
            self.tracker_pool = ConnectionPool(**setting.fdfs_tracker)
            self.timeout  = setting.fdfs_tracker['timeout']
            return None
      
        def __del__(self):
            try:
                self.pool.destroy()
                self.pool = None
            except:
                pass
      
    class fastdfs(object):
        def __init__(self):
            '''
            conf_file:配置文件
            '''
            self.fdfs_client = fastdfsClient()
            self.fdfs_redis = []
            for i in setting.fdfs_redis_dbs:
                self.fdfs_redis.append(redis.Redis(host=i[0], port=i[1], db=i[2]))
      
        def store_by_buffer(self,buf,filename=None,file_ext_name = None):
            '''
            buffer存储文件
            参数:
            filename:自定义文件名,如果不指定,将远程file_id作为文件名
            file_ext_name:文件扩展名(可选),如果不指定,将根据自定义文件名智能判断
            返回值:
            {
            'group':组名,
            'file_id':不含组名的文件ID,
            'size':文件尺寸,
            'upload_time':上传时间
            }
            '''
            if filename and  random.choice(self.fdfs_redis).exists(filename):
                logger.info('File(%s) exists.'%filename)
                return   random.choice(self.fdfs_redis).hgetall(filename)
            t1 = time.time()
    #        try:
            ret_dict = self.fdfs_client.upload_by_buffer(buf,file_ext_name)
    #        except Exception,e:
    #            logger.error('Error occurred while uploading: %s'%e.message)
    #            return None
            t2 = time.time()
            logger.info('Upload file(%s) by buffer, time consume: %fs' % (filename,(t2 - t1)))
            for key in ret_dict:
                logger.debug('[+] %s : %s' % (key, ret_dict[key]))
            stored_filename = ret_dict['Remote file_id']
            stored_filename_without_group = stored_filename[stored_filename.index('/')+1:]
            if not filename:
                filename =stored_filename_without_group
            vmp = {'group':ret_dict['Group name'],'file_id':stored_filename_without_group,'size':ret_dict['Uploaded size'],'upload_time':int(time.time()*1000)}
            try:
                for i in self.fdfs_redis:
                    if not i.hmset(filename,vmp):
                        raise RedisError('Save Failure')
                    logger.info('Store file(%s) by buffer successful' % filename)
            except Exception,e:
                logger.error('Save info to Redis failure. rollback...')
                try:
                    ret_dict = self.fdfs_client.delete_file(stored_filename)
                except Exception,e:
                    logger.error('Error occurred while deleting: %s'%e.message)
                return None
            return vmp
      
        def remove(self,filename):
            '''
            删除文件,
            filename是用户自定义文件名
            return True|False
            '''
            fileinfo = random.choice(self.fdfs_redis).hgetall(filename)
            stored_filename = '%s/%s'%(fileinfo['group'],fileinfo['file_id'])
            try:
                ret_dict = self.fdfs_client.delete_file(stored_filename)
                logger.info('Remove stored file successful')
            except Exception,e:
                logger.error('Error occurred while deleting: %s'%e.message)
                return False
            for i in self.fdfs_redis:
                if not i.delete(filename):
                    logger.error('Remove fileinfo in redis failure')
            logger.info('%s removed.'%filename)
            return True
      
        def download(self,filename):
            '''
            下载文件
            返回二进制
            '''
            finfo = self.getInfo(filename)
            if finfo:
                ret = self.fdfs_client.download_to_buffer('%s/%s'%(finfo['group'],finfo['file_id']))
                return ret['Content']
            else:
                logger.debug('%s is not exists'%filename)
                return None
      
        def list(self,pattern='*'):
            '''
            列出文件列表
            '''
            return random.choice(self.fdfs_redis).keys(pattern)
      
        def getInfo(self,filename):
            '''
            获得文件信息
            return:{
            'group':组名,
            'file_id':不含组名的文件ID,
            'size':文件尺寸,
            'upload_time':上传时间
            }
            '''
            return random.choice(self.fdfs_redis).hgetall(filename)

    配置:

    # -*- coding: utf-8 -*-
    #fastdfs tracker, multiple tracker supported
    fdfs_tracker = {
    'host_tuple':('192.168.2.233','192.168.2.234'),
    'port':22122,
    'timeout':30,
    'name':'Tracker Pool'
    }
    #fastdfs meta db, multiple redisdb supported
    fdfs_redis_dbs = (
        ('192.168.2.233',6379,0),
        ('192.168.2.233',6379,1)
    )
    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    上一篇:Python 如何将一变量做为函数名? 下一篇:pydev部署开发全解析
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【活动】充值PHP中文网VIP即送云服务器• Python pygame入门基础教程• 带你了解Python进程管理神器Supervisor• Python的functools模块使用总结• Python轻量级搜索工具Whoosh的使用(总结分享)• python正则表达式如何实现重叠匹配
    1/1

    PHP中文网