基于django搭建文件存放服务器

在我们的工作中,有的公司不会让你使用阿里云服务器来存放图片或文件,而是要存放在自己的服务器,这就很烦人,但是我们也要搞啊,
models:

from uuid import uuid4
from django.contrib.auth.models import AbstractUser
from django.db import models
from itsdangerous import JSONWebSignatureSerializer as JWSSerializer

# Create your models here.
class User(AbstractUser):
    """用户模型类"""

    mobile = models.CharField(max_length=11, unique=True, verbose_name='手机号')

    class Meta:
        db_table = 'tb_users'
        verbose_name = '用户'
        verbose_name_plural = verbose_name

class Image(models.Model):
    """图片"""

    user_id = models.IntegerField(default=1, verbose_name="上传人id")
    img_name = models.CharField(max_length=252, default="", verbose_name="文件名")
    img_md5 = models.CharField(max_length=128, verbose_name="MD5值")
    img_type = models.CharField(max_length=32, verbose_name="类型")
    img_size = models.IntegerField(verbose_name="图片大小")
    img_path = models.CharField(max_length=128, verbose_name="图片在服务器保存的路径")
    img_url = models.CharField(max_length=128, default='', verbose_name="图片访问url")
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')

    class Meta:
        db_table = 'tb_image'
        verbose_name = '图片信息'
        verbose_name_plural = verbose_name

自己封装的类utils:

import os, time, random, hashlib, datetime, logging, uuid
import magic
from imageserver.utils import conf

logger = logging.getLogger('django')

class PictureStorageToolClass(object):
    """文件存储工具类"""

    def __init__(self):
        pass
        # self.randomlength = 16

    # 计算图片的md5
    def calculate_image_md5(self, file):
        md5_obj = hashlib.md5()
        for chunk in file.chunks():
            md5_obj.update(chunk)
        return md5_obj.hexdigest()
    
    # 检验文件类型(已流的方式判断,可以用于pdf等格式的数据上传)
    def precise_type(self, file):
        """
        类型:
        图片:image/png image/jpeg等,
        文本:text/plain
        pdf:application/pdf
        docx:application/vnd.openxmlformats-officedocument.wordprocessingml.document
        doc:application/msword

        django中file的方法:[image.file, image.field_name, image.name, image.content_type,
                  image.size, image.charset, image.content_type_extra]
        """
        # pip install python_magic来判断文件类型
        # f = magic.Magic(mime=True, uncompress=True)
        # 方式1: buffer_type = f.from_buffer(open('/home/vir/imageserver/image/heading/6f3f73b5-a258-496e-a3df-1bb90d4dd483_5.png', 'rb').read(1024))
        # 方式2: file_type = f.from_file('/home/vir/imageserver/image/heading/6f3f73b5-a258-496e-a3df-1bb90d4dd483_5.png')

        file_type = file.content_type
        return file_type
        
    # 检测文件类型(如果是图片判断后缀)
    def judge_type(self, image_type):
        image_type_list = ['jpeg','jpg','png','pdf','tga','tif','svg','gif','bmp']
        if image_type not in image_type_list:
            return 0
        return 1
       
    # 限制文件大小(5M)
    def file_size(self, image_size): 
        if image_size > conf.IMAGE_SIZE:
            return 0
        return 1
    
    # 返回图片存储路径
    def storage_path(self, folder):
        """
        folder:
        图片存储文件路径文件夹命名:
        头像图片:heading
        面料图片:fabric
        作品图片:style
        其他图片:rest
        """
        root_path = os.path.join(conf.IMAGE_STORAGE_PATH, folder)
        # 如果没有创建
        if not os.path.exists(root_path):
            os.makedirs(root_path)
            
        return root_path
    
    # 保存图片
    def save_iamge(self, root_path, image_name, image):
        """
        root_path: 图片路径
        image_name:图片名称
        iamge:前端传递过来的image对象
        """
        uuid_iamge_name = None
        file_path_name = None

        # 保存
        try:
            uuid_iamge_name = str(uuid.uuid4()) + '_' + image_name  # 生成一个新的不重复的名称
            file_path_name = root_path + '/' + uuid_iamge_name  # 图片的完整路径(仅仅是在我们服务器保存的位置,不是我们外部访问的路径)

            with open(file_path_name, 'wb') as f:
                for line in image.chunks():
                    f.write(line)
        except Exception as e:
            logger.error(e)
            return 0, 0
        
        return uuid_iamge_name, file_path_name

view中:

import datetime, json, uuid, base64
import django_redis, requests, logging
from django.db import transaction
from django.db.models import F
from django.contrib.auth.hashers import make_password
from rest_framework.generics import CreateAPIView, ListAPIView, UpdateAPIView, DestroyAPIView, RetrieveAPIView, GenericAPIView
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import BasicAuthentication, TokenAuthentication, SessionAuthentication
from django.contrib.auth.models import AnonymousUser
from django.contrib.auth import login, logout
from django.http import HttpResponse

from imageserver.utils.api_response import APIResponse
from imageserver.utils.ex_serializer import serializser_exception
from imageserver.utils import conf
from users.models import Image
from users.utils import PictureStorageToolClass

logger = logging.getLogger('django')

# Create your views here.
class TokenAPIView(GenericAPIView):
    """获取token"""
    authentication_classes = []

    def get(self, request):

        # user_id = request.user.id
        user_id = 1
        
        # token为uuid
        token = str(uuid.uuid4())
        # 将token保存到redis中
        redis_conn = django_redis.get_redis_connection('token')
        redis_conn.setex('image_token_{}'.format(user_id), conf.TOKEN_TIME, token)


        return APIResponse.success(data=token, message="获取成功")

class UploadingImageAPIView(GenericAPIView):
    """上传图片"""
    authentication_classes = []

    def post(self, request):

        # user_id = request.user.id
        user_id = 1

        token = request.data.get('token', None)
        image = request.FILES.get('image', None)
        folder = request.data.get('folder', 'heading') # 保存的文件夹
        
        if not token:
            return APIResponse.fail(message="缺少参数token")

        if not image:
            return APIResponse.fail(message="图片未上传!")

        # 获取redis中的token
        redis_conn = django_redis.get_redis_connection('token')
        real_image_token = redis_conn.get('image_token_{}'.format(user_id))
        
        # token失效
        if real_image_token is None:
            return APIResponse.failure()

        real_iamge_token = real_image_token.decode()
        if token != real_iamge_token:
            return APIResponse.fail(message="token错误!")

        image_md5 = PictureStorageToolClass().calculate_image_md5(image)
        image_obj = Image.objects.filter(img_md5=image_md5).first()

        # 如果上传的图片不重复
        if not image_obj:
            # 判断文件大小
            image_size = image.size
            image_name = image.name
            res_size = PictureStorageToolClass().file_size(image_size)
            if not res_size:
                return APIResponse.fail(message="文件大小不能超过5M!")

            # 判断文件类型
            image_type = PictureStorageToolClass().precise_type(image)
    
            # 如果是图片, 判断图片后缀
            if image_type.split('/')[0] == 'image':
                image_type = image_name.split('.')[-1]
                res_type = PictureStorageToolClass().judge_type(image_type)
                if not res_type:
                    return APIResponse.fail(message="图片后缀错误!")

            # 定义图片存储路径
            root_path = PictureStorageToolClass().storage_path(folder)

            # 保存文件
            uuid_iamge_name, img_path = PictureStorageToolClass().save_iamge(root_path, image_name, image)

            if not uuid_iamge_name:
                return APIResponse.fail(message="保存文件失败,请重试!")

            # 外部访问的路径(因为在总url中写了要以image开头)
            outside_url = '/image/' + folder + '/' + uuid_iamge_name

            # 表保存数据
            Image.objects.create(
                user_id = 1,
                img_name=image_name,
                img_md5=image_md5,
                img_type=image_type,
                img_size=image_size,
                img_path=img_path,
                img_url=outside_url,
            )

            # 外面访问的完整路径(包括域名)
            entirely_outside_url = conf.TEST_DOMAIN_NAME + outside_url

            return APIResponse.success(data=entirely_outside_url, message="上传成功")

        # 如果上传的图片重复,则直接返回路径
        else:
            image_url = conf.TEST_DOMAIN_NAME + image_obj.img_url
            return APIResponse.success(data=image_url, message="上传成功")


conf中:

# 项目所需要的参数

# token过期时间
TOKEN_TIME = 24*3600


# 图片大小 200 * 1024 # (200kb)
IMAGE_SIZE = 1024 * 1024 * 5 # 5M

# 图片存储路径(绝对路径)
IMAGE_STORAGE_PATH = '/home/vir/imageserver/image/'

# 测试服域名
TEST_DOMAIN_NAME = "http://47.xxx.xx.xx:10000"

# 正式服域名
DOMAIN_NAME = ""

setting中:

# 图片访问目录
MEDIA_ROOT =  os.path.join(os.path.dirname(os.path.dirname(BASE_DIR)), "image")


CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/4",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            # "PASSWORD": "im_zhang"
        }
    },
    # token
    "token": {
                "BACKEND": "django_redis.cache.RedisCache",
                "LOCATION": "redis://127.0.0.1:6379/5",
                "OPTIONS": {
                    "CLIENT_CLASS": "django_redis.client.DefaultClient",
                }
            },
    # session状态保持
    "session": {
                "BACKEND": "django_redis.cache.RedisCache",
                "LOCATION": "redis://127.0.0.1:6379/6",
                "OPTIONS": {
                    "CLIENT_CLASS": "django_redis.client.DefaultClient",
                }
            },
}

users模块中urls:

from django.urls import path
from rest_framework import routers
from . import views

urlpatterns = [
    path('token', views.TokenAPIView.as_view()),
    path('uploading', views.UploadingImageAPIView.as_view()),

]

总urls中:

import os
from django.urls import re_path,include
from imageserver.settings.base import MEDIA_ROOT
from django.conf.urls.static import serve

# 注意:MEDIA_ROOT = /home/vir/imageserver/image, 外面的url 域名/image/ 就相当于/home/vir/imageserver/image

urlpatterns = [
    re_path(r'^user/', include('users.urls')),
    # 图片资源
    re_path(r'^image/(?P<path>.*)', serve, {"document_root":MEDIA_ROOT}),
]

访问图片:
在这里插入图片描述
数据库存放数据:
在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值