utils文件
FDFS_storage.py
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)
# tmp拼接后缀
imgdir = f'{os.path.join(settings.BASE_DIR, "utils/fastDFS/")}/{str(int(time.time() * 10000))}.{str(name).split(".").pop()}'
with open(imgdir, 'wb+') as f:
# for chunk in content.read().chunks():
# f.write(chunk)
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(result)
return filename # 返回地址路径字符串
def url(self, name):
'''
返回文件的完整路径
:name 数据库中保存的文件
'''
return self.base_url + name
def exists(self, name): # FastDfs可以自己解决文件重名,
'''
:param name:文件名
:return: False
'''
return False # 如果给定名称所引用的文件已经存在于存储系统中,则返回 True;如果该名称可用于新文件,则返回 False。
def delete(self, name):
try:
conf_path = get_tracker_conf(self.client_conf)
client = Fdfs_client(conf_path)
if not isinstance(name, bytes):
name = bytes(name, encoding="utf-8")
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() # 返回删除的图片路径字符串
client.conf
connect_timeout = 30
network_timeout = 60
tracker_server = 121.5.109.75:22122
http.tracker_server_port = 8088
test.py
# from fdfs_client.client import Fdfs_client
# from fdfs_client.client import get_tracker_conf, Fdfs_client
# tracker_conf_path = get_tracker_conf("D:/MyServer/o_server/o_project/utils/fastDFS/client.conf") # tracker配置文件的地址
# client = Fdfs_client(tracker_conf_path)
# 上传图片
# ret = client.upload_by_filename(r"D:/MyServer/o_server/o_project/test_1670059698725128.5.html")
# byte数据
# ret = client.upload_by_buffer(r"D:/MyServer/o_server/o_project/test_1670059698725128.5.html")
# print(ret)
# 删除图片
# file_id = ret["Remote file_id"] # 删除图片需要上传时返回的id
# ret = client.delete_file(file_id)
# print(ret)
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路径
MEDIA_ROOT = 'http://121.5.109.75:8088/'
视图
# 日记序列化器
class NoteDeserialization(serializers.ModelSerializer):
class Meta:
model = Diary
fields = '__all__'
depth = 1
# 序列化后的钩子
def to_representation(self, instance):
data = super().to_representation(instance)
data['status'] = [status[1]
for status in Diary.status_choice if data['status'] == status[0]][0]
if data['image']:
data['image'] = str(data['image']).split('/')[-1]
return data
# 日记反序列化器
class NoteSerializers(serializers.ModelSerializer):
upload_image = serializers.ListField(
write_only=True, label="上传图片列表", required=False)
class Meta:
model = Diary
fields = '__all__'
# 自定义验证
def validate(self, attrs):
# 图片上传验证处理
redis_conn = get_redis_connection('default')
request = self.context.get('request')
# 去除content保留存在的图片
if images_list := attrs.get('upload_image'):
content = attrs.get('content')
for i in images_list:
if re.search(i, content, re.S):
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()
for image in delete_image_list:
fdfs.delete(image)
redis_conn.delete("upload_image_%s" % request.user.id)
# 解析ip
attrs['ip'] = get_client_ip(request)
attrs['ip_info'] = analysis_ip_info(attrs['ip'])
attrs.pop('upload_image', '')
return attrs
# 处理添加修改完成后的tag多对多序列化
def to_representation(self, instance):
data = super().to_representation(instance)
validated_data = {}
try:
validated_data = self.validated_data
except AssertionError as e:
print(e)
if tag := validated_data.get('tag'):
data['tag'] = toDict(tag)
else:
data['tag'] = list(Tag.objects.filter(id__in=data.get('tag')).values())
if author := validated_data.get('author'):
data['author'] = toDict(author)
else:
data['author'] = list(User.objects.filter(id=data.get('author')).values())
return data
def update(self, instance, validated_data):
return super().update(instance, validated_data)
# 日记视图(有分页)
class NoteModelViewSet(ModelViewSet):
queryset = Diary.objects.all()
serializer_class = NoteSerializers
pagination_class = NoteLimitOffsetPagination
def get_serializer_class(self):
if request := getattr(self, 'request'):
if request.method == 'GET':
return NoteDeserialization
return NoteSerializers
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(i)
instance.delete()
# /note/notemodel/get_random_diary/ 随机3个
@action(methods=['get'], detail=False)
def get_random_diary(self, request):
queryset = self.get_queryset()
random_list = random.sample(range(0, len(queryset)), 3)
hot_queryset = [queryset[i] for i in random_list]
ser = self.get_serializer(instance=hot_queryset, many=True)
return Response(ser.data, status=200)
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
data = serializer.data
# 添加点赞数量
instance.click = F('click') + 1
instance.save()
return Response(data)
# 删除日记内容中的图视图
class NoteImageUpload(APIView):
def post(self, request):
data = request.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.__str__,
}
return Response(data=message, content_type="application/json")