当前位置:
首页 > Python基础教程 >
-
FastDFS加Redis实现自定义文件名存储海量文件
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下载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
# -*- 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) |
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比
一款纯 JS 实现的轻量化图片编辑器
关于开发 VS Code 插件遇到的 workbench.scm.
前端设计模式——观察者模式
前端设计模式——中介者模式
创建型-原型模式