fastdfs client庫windows調用報錯報錯坑1:
ModuleNotFoundError: No module named 'mutagen._compat'
解決辦法:找到utils.py文件修復導入報問題如下:
from mutagen._senf._compat import StringIO

坑2:進行settings.py client.conf配置路徑settings.FASTDFS_CLIENT_CONF引入報錯,實際上我已經配置了在settings.py很狗血:
Requested setting , but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.
解決辦法:
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mall.settings')
坑三: fastdfs 使用方法client.upload_by_file()報錯Error: 'sendfile' system call only available on linux.此方法只能在linux系統使用windows使用錯誤
client=Fdfs_client(conf_path=conf)
client.upload_by_file()
解決辦法:使用client.upload_by_buffer(data.read())方法調試:
conf=settings.FASTDFS_CLIENT_CONF
client=Fdfs_client(conf_path=conf)
with open('hi.png','rb')as data:
ret=client.upload_by_buffer(data.read())
print(ret)
if ret.get("Status")!="Upload successed.":
raise Exception('fastdfs upload failed !!!')
file_name = ret.get("Remote file_id")
print(file_name)
fastdfs 自定義存儲實現如下:
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mall.settings')
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):
"""
org-doc: https://www.pianshen.com/article/1299301803/
:param server_url: fast-dfs服務器ip:port
:param: client_conf
"""
def __init__(self, server_url=None, client_conf=None):
if not server_url:
self.server_url = server_url
self.server_url = settings.FASTDFS_URL
if not client_conf:
self.client_conf = client_conf
self.client_conf = settings.FASTDFS_CLIENT_CONF
def _open(self, name, mode='rb'):
""" for read file : Retrieve the specified file from storage."""
return super().open(name,'rb')
def _save(self, name, content, max_length=None):
client=Fdfs_client(conf_path=self.client_conf)
extend_name=name.split('.')[-1]
ret=client.upload_by_buffer(content.read(),file_ext_name=extend_name)
if ret.get("Status")!="Upload successed.":
raise Exception('fastdfs upload failed !!!')
file_name = ret.get("Remote file_id").replace('\\','/')
print('測試獲取文件路徑',file_name)
return file_name
def url(self, name):
path=self.server_url+'/'+name
return path
def exists(self, name):
""" means filename always is available is new filename, fast-dfs always storage one same file """
return False
接下來,使用drf編寫模型類,序列化器以及view視圖測試代碼:
model如下:
class FileModels(models.Model):
file_id=models.AutoField(primary_key=True,max_length=200)
file=models.FileField()
class Meta:
db_table='tbl_file'
verbose_name='文件測試'
序列化器如下:
from rest_framework import serializers
from .models import FileModels
class SerialFiles(serializers.ModelSerializer):
class Meta:
model = FileModels
fields = "__all__"
視圖:
class FileView(APIView):
def post(self, request, *args, **kwargs):
print(request.POST)
print(request.data)
ser = SerialFiles(data=request.data, many=False)
if ser.is_valid():
ser.save()
return Response(data=ser.data, status=status.HTTP_200_OK)
else:
return Response(data=ser.errors, status=status.HTTP_404_NOT_FOUND)
def get(self,request, *args, **kwargs):
inst=FileModels.objects.all()
ser=SerialFiles(instance=inst,many=True)
return Response(data=ser.data)
注意事項起初,我用postman form-data 上傳一直報參數f不存在,匪夷所思,懷疑是沒有指明使用muti-formdata導致的,於是使用html寫個簡單的上傳表單提交文件,果然

最后點擊上傳可以看到頁面返回:

最后送上我的表單html上傳文件基於 enctype=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1><div align=center style="color:green" display="block"> hello world </div><p> this is a first text for test web page and we will sart from hello </p></h1>
<form action="http://127.0.0.1:8000/file/" method="post" enctype="multipart/form-data">
<input type="file" name='file'>
<!--input type="file" name='file22'-->
<!--input type="username" name='username'-->
<!--input type="password" name='password'-->
<!--input type="file" accept=".xlsx"-->
<input type="submit" value="提交">
</form>
</body>
© codemao
</html>
結語:其實自定義文件儲存,在企業使用是非常多的oss,fastdfs,minio ,qiniu雲,騰訊cos等等,django默認自帶FilesystemStorage,實現一般存在django項目所在服務器時間久了影響磁盤空間以及性能,企業很少這樣做實際開發
關於minio自定義存儲可以這樣:
可能遇到的坑:
第一:settings 定義MINIO_CONF找不到,我用的django2.2,好像必須要大寫變量在settings
事實上我已經導入了
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mall.settings')
坑2:save方法返回name是文件名,不然找不到,其次我在上傳minio是object_name修改了用了uuid加隨機字符串避免多用戶使用上傳文件名相同覆蓋問題
def _save(self, name, content, max_length=None):
if not self.minio_client.bucket_exists(self.bucket_name):
self.minio_client.make_bucket(self.bucket_name)
try:
self.minio_client.get_bucket_policy(self.bucket_name)
except NoSuchBucketPolicy:
self.set_bucket_policy_public(self.bucket_name)
(etag, vid)=self.minio_client.put_object(bucket_name=self.bucket_name,object_name=name,data=content,length=content.size)
if etag:
print("save return minio is {}".format('/{}/{}'.format(self.bucket_name,name)))
# 這里返回name,就是url方法的入參name
return name
送上settings配置:
DEFAULT_FILE_STORAGE='utils.minio_fdfs.minio_storage.MinioStorage'
MINIO_CONF = {
'endpoint': '192.168.110.151:9000',
'access_key': 'admin',
'secret_key': 'admin123456',
'secure': False
}
BUCKET_NAME='mybucket'
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mall.settings')
from django.conf import settings
from django.core.files.storage import Storage
from django.utils.deconstruct import deconstructible
from minio import Minio, ResponseError
from minio.error import NoSuchBucketPolicy
import json
import uuid
import random
import string
@deconstructible
class MinioStorage(Storage):
"""
python docs: https://docs.min.io/docs/python-client-api-reference.html
@:param fp : fileobject
@:param object_name is the object name which will save to minio bucket
@:param bucket_name the bucket name of minio .
"""
def __init__(self, bucket_name=None, object_name=None):
if not settings.MINIO_CONF:
raise ValueError('required MINIO_CONF config in django.settings: format is:\n{}'.format(
{'endpoint': '192.168.110.151:9000',
'access_key': 'username',
'secret_key': 'password',
'secure': False,
}
))
self.minio_conf = settings.MINIO_CONF
if not settings.BUCKET_NAME:
self.bucket_name = bucket_name
self.bucket_name=settings.BUCKET_NAME
self.object_name = object_name
self.endpoint_minio=settings.MINIO_CONF.get('endpoint',None)
self.minio_client= Minio(**self.minio_conf)
def _open(self, name, mode='rb'):
""" for read file : Retrieve the specified file from storage."""
return super().open(name, 'rb')
def _save(self, name, content, max_length=None):
filename, extendname = name.split('.')
salt= ''.join(random.sample(string.ascii_letters + string.digits, 7))
name = name.replace(filename, uuid.uuid1().hex +salt+'_' +filename)
if not self.minio_client.bucket_exists(self.bucket_name):
self.minio_client.make_bucket(self.bucket_name)
try:
self.minio_client.get_bucket_policy(self.bucket_name)
except NoSuchBucketPolicy:
self.set_bucket_policy_public(self.bucket_name)
(etag, vid)=self.minio_client.put_object(bucket_name=self.bucket_name,object_name=name,data=content,length=content.size)
if etag:
# print("save return minio is {}".format('/{}/{}'.format(self.bucket_name,name)))
# 這里返回name,就是url方法的入參name
name_file='{}/{}'.format(self.bucket_name,name)
return name_file
def url(self, name):
print("url minio return is {}".format(self.endpoint_minio +'/{}/{}'.format(self.bucket_name,name)))
return self.endpoint_minio +'/'+name
def exists(self, name):
""" means filename always is available is new filename, fast-dfs always storage one same file """
return False
def set_bucket_policy_public(self,bucket_name):
"""set file to public download by url: http:endpoint/bucket_name/object-name"""
policy = {'Version': '2012-10-17', 'Statement': [{'Effect': 'Allow',
'Principal': {'AWS': ['*']},
'Action': ['s3:GetBucketLocation', 's3:ListBucket'],
'Resource': ['arn:aws:s3:::{}'.format(bucket_name)]},
{'Effect': 'Allow', 'Principal': {'AWS': ['*']},
'Action': ['s3:GetObject'],
'Resource': ['arn:aws:s3:::{}/*'.format(bucket_name)]}]}
# set bucket to public download
self.minio_client.set_bucket_policy(bucket_name=bucket_name, policy=json.dumps(policy))
def get_minio_object(self,bucket_name,object_name):
""" :return minio object of file,if get bytes by read()"""
response=None
try:
response=self.minio_client.get_object(bucket_name=bucket_name,object_name=object_name)
finally:
response.close()
response.release_conn()

去minio查看上傳情況:

下一篇docker 部署minio(https://www.cnblogs.com/SunshineKimi/p/13975581.html),對比了下fastdfs,我還是喜歡minio,真的很爽:
數據庫存儲可以帶文件拓展名;第一條是minio,第二條fastdfs

