在我们的工作中,有的公司不会让你使用阿里云服务器来存放图片或文件,而是要存放在自己的服务器,这就很烦人,但是我们也要搞啊,
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}),
]
访问图片:
数据库存放数据: