django restframework+fastdfs 上传文件

fastdfs client库windows调用报错报错坑1:

ModuleNotFoundError: No module named 'mutagen._compat'
解决办法:找到utils.py文件修复导入报问题如下:
from mutagen._senf._compat import StringIO

django restframework+fastdfs 上传文件

 

 

坑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)
        ret=client.upload_by_buffer(content.read())
        print(ret)
        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写个简单的上传表单提交文件,果然

django restframework+fastdfs 上传文件

 

 

最后点击上传可以看到页面返回:

django restframework+fastdfs 上传文件

 

 数据库检查:

django restframework+fastdfs 上传文件

 

 

最后送上我的表单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>
&copy codemao
</html>

 

结语:其实自定义文件储存,在企业使用是非常多的oss,fastdfs,minio ,qiniu云,腾讯cos等等,django默认自带FilesystemStorage,实现一般存在django项目所在服务器时间久了影响磁盘空间以及性能,企业很少这样做实际开发

关于minio自定义存储可以这样:

 

 

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  os
import filetype
@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
        self.bucket_name = 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):
        # minio_client = Minio(**self.minio_conf)
        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.read(),
                            length=os.stat(name).st_size)
        if etag:
            return '/{}/{}'.format(self.bucket_name,name)

    def url(self, name):

        return self.endpoint_minio +'/{}/{}'.format(self.bucket_name,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()

if __name__ == '__main__':
    import requests

    confs={'endpoint': '192.168.110.151:9000',
     'access_key': 'admin',
     'secret_key': 'admin123456',
     'secure': False,
     }
    cli=Minio(**confs)
    fp=cli.get_object('mybucket',object_name='testcond.xlsx')

    files = [
        ("file",("testcond.xlsx", fp))
             ]
    datas={"name":1338888}
    # 'http://httpbin.org/post'
    r=requests.post(url='http://127.0.0.1:8000/testfile/',data=datas,files=files)
    print(r.text)
    # print(filetype.guess_mime(fileobject))

  

上一篇:docker中使用fastDFS


下一篇:FastDFS常见问题