\安装 pip install py3Fdfs 客户端
虚拟机环境
- 桥接模式, 使用固定
#安装
docker pull delron/fastdfs
# 启动调度服务器
docker run -dit --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs tracker
# 启动存储服务器
docker run -dit --network=host --name storage -e TRACKER_SERVER=192.168.10.3:22122 -v /var/fdfs/storage:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs storage
# 防火墙开放22122 23000 8888 三个端口
# 总日志
/var/fdfs/logs
# storage
/var/fdfs/storage/logs
# 1台tracker调度服务器, 多台storage存储服务器的方案(storage在同一个组中, 组中全部成员资料都会复制)
docker run -dit --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs tracker
docker run -dit --network=host --name storage -e TRACKER_SERVER="tracker主机地址":22122 -v /var/fdfs/storage:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs storage
docker run -dit --network=host --name storage -e TRACKER_SERVER="tracker主机地址":22122 -v /var/fdfs/storage:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs storage
python客户端基本使用
from fdfs_client.client import Fdfs_client,get_tracker_conf
conf_path = get_tracker_conf("/home/just/Django02/FlaskIhome/ihome/utils/fdfs/client.conf")
client = Fdfs_client(client=conf_path)
# 上传图片
ret = client.upload_by_filename("./20191023111957.png") # 上传图片名
# ret = client.upload_by_buffer() 上传byte数据
print(ret)
# 删除图片
file_id = ret["Remote file_id"] # 删除图片需要上传时返回的id
ret = client.delete_file(file_id) # file_id = b"xxxxxxxxxxxxx"
print(ret)
案例
- 重写django文件储存类
from fdfs_client.client import Fdfs_client , get_tracker_conf
from django.conf import settings
from django.core.files.storage import Storage
from django.utils.deconstruct import deconstructible
import os
import logging
log = logging.getLogger(__name__)
class FastDFSStorage(Storage):
def __init__(self,base_url=None,client_conf=None):# 构造路径,服务器配置文件路径
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 save(self, name, content, max_length=None):
'''save方法'''
conf_path = get_tracker_conf(self.client_conf)
client = Fdfs_client(conf_path)
imgdir = f'{os.path.join(settings.BASE_DIR, "utils/fastdfs")}/tmp.{str(name).split(".")[1]}' # tmp拼接后缀
with open(imgdir,'wb+') as f:
f.write(content.read()) # 读取文件数据写入f
result = client.upload_by_filename(imgdir) # 写入后上传至数据库
os.remove(imgdir) # 删除刚刚本地创建的文件
if result.get('Status') != 'Upload successed.': # 如果返回结果
logging.error('上传文件到FastDFS失败')
raise Exception('上传文件到FastDfs失败')
filename = result.get('Remote file_id') # 服务器返回的地址路径Byte类型
return filename.decode() # 返回地址路径字符串
def url(self, name):
'''
返回文件的完整路径
:name 数据库中保存的文件
'''
return self.base_url + name
def exists(self, name): # FastDfs可以自己解决文件重名,
'''
:param name:文件名
:return: False
'''
return False # 返回False,告诉Django 上传的都是新文件
def delete(self, name):
conf_path = get_tracker_conf(self.client_conf)
client = Fdfs_client(conf_path)
result = client.delete_file(name)
if 'Delete file successed.' == result[0]: # 如果返回结果
logging.error('删除文件到FastDFS失败')
raise Exception('删除文件到FastDfs失败')
return result[1].decode()
- settings.py
# FDFS配置
# 自定义文件系统
# django文件存储
DEFAULT_FILE_STORAGE = 'utils.fastDFS.FDFS_storage.FastDFSStorage'
FDFS_URL = "http://121.5.109.75:8088/"
FDFS_CLIENT_CONF = os.path.join(BASE_DIR, 'utils/fastDFS/client.conf') # conf路径
- view视图使用
from django_china.utils.fastdfs.fdfd_storage import FastDFSStorage
def xxxx():
file = request.FILES.get('file')
if not file:
return http.JsonResponse({'code':4001,'errmsg':'没有图片数据'})
filename = file.name
content = file.file
try:
fafs = FastDFSStorage()
image_url = fafs.save(name=filename,content=content)
except Exception as e:
logging.error(e)
return http.JsonResponse({'code':4001,'errmsg':'储存用户图片失败'})
try:
User.objects.filter(id=user_id).update(url=image_url)
except Exception as e:
logging.error(e)
fafs.delete(bytes(image_url))
return http.JsonResponse({'code': 4001, 'errmsg': '数据库储存用户图片失败'})
案例2
- 视图
class NoteSerializers(serializers.ModelSerializer):
upload_image = serializers.ListField(write_only=True, label="上传图片列表")
class Meta:
model = Diary
fields = '__all__'
def validate(self, attrs):
redis_conn = get_redis_connection('default')
request = self.context.get('request')
if images_list := attrs.get('upload_image'):
content = attrs.get('content')
print("文章上传的图片:",images_list)
for i in images_list:
# 查找用户上传的图片是否存在于文本中
if re.search(i, content, re.S): # 如果图片还存在于文本中 = 1
# redis中去除这些图片(在列表中删除了, 后面就不会执行delete操作)
print("列表中准备去除的图片:", i)
redis_conn.lrem("upload_image_%s" % request.user.id, 0, i)
# 剩下redis列表中没删除的图片, 全部执行delete()
if delete_image_list := redis_conn.lrange("upload_image_%s" % request.user.id, 0, -1):
fdfs = FastDFSStorage()
print("redis准备删除的图片:",delete_image_list)
# 删除列表图片
for image in delete_image_list:
print(image)
fdfs.delete(image)
# 删除列表
redis_conn.delete("upload_image_%s" % request.user.id)
print("upload_image")
del attrs['upload_image']
return attrs
# 日记视图
class NoteModelViewSet(ModelViewSet):
queryset = Diary.objects.all()
serializer_class = NoteSerializers
pagination_class = NoteLimitOffsetPagination
def perform_destroy(self, instance):
if upload_image_list := re.findall(f'{settings.FDFS_URL}(.*?)"', instance.content, re.S):
print("准备删除的图片:", upload_image_list)
fdfs = FastDFSStorage()
for i in upload_image_list:
fdfs.delete(bytes(i, encoding="utf-8"))
instance.delete()
class NoteImageUpload(APIView):
def post(self, request):
data = request.data
print(data)
image = data.get('uploadImage')
fdfs = FastDFSStorage()
try:
upload_name = fdfs.save(name=image.name, content=image.file)
redis_conn = get_redis_connection('default')
redis_conn.lpush("upload_image_%s" % request.user.id, upload_name)
message = {
"errno": 0, # 是数字
"data": {
"url": settings.FDFS_URL + upload_name, # 图片src, 必须
"alt": "image_alt", # 图片描述, 非必须
"href": "image_href", # 图片的链接,非必须
"upload_name": upload_name
}
}
except Exception as e:
print(e)
message = {
"errno": 1,
"message": e,
}
return Response(data=message, content_type="application/json")
- 重写的文件存储系统
from fdfs_client.client import Fdfs_client , get_tracker_conf
from django.conf import settings
from django.core.files.storage import Storage
from django.utils.deconstruct import deconstructible
from django_redis import get_redis_connection
from fdfs_client.exceptions import DataError
import os
import logging
import time
logger = logging.getLogger(__name__)
class FastDFSStorage(Storage):
def __init__(self,base_url=None,client_conf=None):# 构造路径,服务器配置文件路径
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 save(self, name, content, max_length=None):
'''save方法'''
conf_path = get_tracker_conf(self.client_conf)
client = Fdfs_client(conf_path)
imgdir = f'{os.path.join(settings.BASE_DIR, "utils/fastDFS/")}/{str(int(time.time() * 10000))}.{str(name).split(".").pop()}' # tmp拼接后缀
with open(imgdir,'wb+') as f:
f.write(content.read()) # 读取文件数据写入f
result = client.upload_by_filename(imgdir) # 写入后上传至数据库
os.remove(imgdir) # 删除刚刚本地创建的文件
if result.get('Status') != 'Upload successed.': # 如果返回结果
logger.error('上传文件到FastDFS失败')
raise Exception('上传文件到FastDfs失败')
filename = result.get('Remote file_id').decode() # 服务器返回的地址路径Byte类型
print(filename)
return filename # 返回地址路径字符串
def url(self, name):
'''
返回文件的完整路径
:name 数据库中保存的文件
'''
return self.base_url + name
def exists(self, name): # FastDfs可以自己解决文件重名,
'''
:param name:文件名
:return: False
'''
return False # 返回False,告诉Django 上传的都是新文件
def delete(self, name):
try:
conf_path = get_tracker_conf(self.client_conf)
client = Fdfs_client(conf_path)
result = client.delete_file(name)
print(result)
if 'Delete file successed.' == result[0]: # 如果返回结果
print("成功删除图片:",result[1])
else:
logger.error('删除文件到FastDFS失败')
raise Exception('删除文件到FastDfs失败')
except DataError as e:
print(e)
return "Error: 2, No such file or directory"
return result[1].decode() # 返回删除的图片路径字符串
- setting.py配置文件
# FDFS配置
# 自定义文件系统
# django文件存储
DEFAULT_FILE_STORAGE = 'utils.fastDFS.FDFS_storage.FastDFSStorage'
FDFS_URL = "http://121.5.109.75:8088/"
FDFS_CLIENT_CONF = os.path.join(BASE_DIR, 'utils/fastDFS/client.conf') # conf路径
- client.conf配置
connect_timeout = 30
network_timeout = 60
tracker_server = 192.168.50.225:22122 # 主机ip
http.tracker_server_port = 8888 # nginx端口号默认8888