前面我写了个简单的图片上传服务器功能:Django实战009:文件上传实现过程详解,这里只是简单的实现了图片上传、存储和访问的功能,并没有对图片进行过多的处理,导致大部分图片出现重复存储,文件名重命名等现象,所以今天来重新搭建图片服务器。
重新定义模型类
定义模型来保存图片的基本信息,包括图片的名字、大小、类型、文件存储路径和创建时间,这里我还定义了一个Md5用来区分图片,防止重复上传相同的图片。更新时间看个人需求,我这里暂时不需要就注释掉了(我主要用来存储文章中的图片的,如果已经存在直接返回路径即可,无需更新文件)。
定义完之后记得执行迁移命令,同步至数据库:Python manage.py makemigrations 和 python manage.py migrate
class uploadImage(models.Model):
imgName = models.CharField(max_length=252, default="" ,verbose_name="文件名")
imgMd5 = models.CharField(max_length=128,verbose_name="MD5值")
imgType = models.CharField(max_length=32,verbose_name="类型")
imgSize = models.IntegerField(verbose_name="大小")
imgPath = models.CharField(max_length=128, verbose_name="图片路径")
imgCreated = models.CharField(max_length=64,default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),verbose_name="创建时间")
# imgUpdated = models.CharField(max_length=64,default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),verbose_name="更新时间")
def __str__(self):
return self.imgName
class Meta:
db_table = 'uploadImage'
注意:这里更新时间我用的是CharField而非DateField,原因是使用DateField时时间格式并不是我想要的,通过strftime转换后就成了字符串,所以我直接用CharField来存了。文件路径存储用到的也不是ImageField,而是直接用CharField来存储的。
定义路由
为视图配置访问路由,我定义了一个uploadimage,我们只要访问http://127.0.0.1:8000/uploadimage/就可以找到对应的路由了,因为这是POST请求接口,需要携带数据进行接口访问,不然就会报错,这里我们可以通过postman来模拟测试。
from django.conf.urls import url
from article import views
urlpatterns = [
url(r'^uploadimage/$',views.uploadImg),
]
定义方法
我单独新增了一个文件tools.py来写入以下方法:包括 计算文件的md5、#文件重命名及写入、# 检测文件类型、# 限制文件大小,方便后面在视图中调用
import os, time, random,hashlib,datetime
from django.conf import settings
# 计算文件的md5
def GetFileMd5(file):
md5Obj = hashlib.md5()
for chunk in file.chunks():
md5Obj.update(chunk)
return md5Obj.hexdigest()
#文件重命名及写入
def Rename(file):
times=time.strftime('%Y%m%d%H%M%S')
ran=random.randint(0,1000)
ext = os.path.splitext(file.name)[1]
newfile="{}{}{}".format(times,ran,ext)
path=os.path.join('media',newfile).replace('\\','/')
read=open(path, 'wb+')
for chunk in file.chunks():
read.write(chunk)
read.close()
return path
# 检测文件类型
def JudgeType(ext):
ImageType = [".png", ".jpeg", ".jpg", ".gif", ".bmp"]
if ext in ImageType:
return True
return False
# 限制文件大小
def FileSize(size):
limit=settings.IMAGE_SIZE_LIMIT
if size<limit:
return True
return False
定义视图
我要实现的功能有以下几点:
1.获取文件MD5值,用来区分文件,防止重复上传相同的图片。
2.文件大小限制,一般情况下允许上传不大于5M的图片。
3.文件类型检测,判断上传文件是否为图片类型文件。
4.文件重命名,我们在上传图片的时候经常会有同名的文件,为了更好的区分我们在上传的时候对文件进行重命名操作。
5.手动上传图片文件到服务器中。
6.将信息存入到数据库中并返回图片地址给前端。
接下来我们在视图中引入,先获取前端发来的文件对象,然后计算出文件的MD5,那这个MD5去数据库中匹配,如果存在则直接将对应的图片路径返回给前端,如果没有我们就开始进行写入,写入前我们先对文件大小进行判定,再对文件类型进行判定,如果都满足的话就进行文件重命名和写入操作,最后将文件信息存入到数据库中即可。这里我提取了文件重命名之后的名称、文件类型、文件大小、文件存储路径和文件MD5值,创建时间是自动写入的无需操作。
from django.http import JsonResponse
from article import models
from article.models import uploadImage
from article.tools import *
from django.conf import settings
# Create your views here.
def uploadImg(request):
if request.method == "POST":
file = request.FILES.get('img')
md5=GetFileMd5(file)
imgobj = models.uploadImage.objects.filter(imgMd5=md5)
if not imgobj:
size = file.size
if not FileSize(size):
info = {'code': 403, 'error': '文件太大!'}
return JsonResponse(info)
ext = os.path.splitext(file.name)[1]
if not JudgeType(ext):
info = {'code': 403, 'error': '文件类型错误!'}
return JsonResponse(info)
path = Rename(file)
name = os.path.basename(path)
create=models.uploadImage.objects.create(imgName=name, imgMd5=md5,imgType=ext,imgSize=size,imgPath=path)
url='http://'+settings.HOST_NAME +"/"+create.imgPath
info={'code':200,'imgurl':url}
return JsonResponse(info)
else:
url ='http://'+settings.HOST_NAME +"/"+imgobj.first().imgPath
info = {'code': 200, 'imgurl': url}
return JsonResponse(info)
接口测试
通过postman模拟POST请求来测试该接口,我们提交一个文件类型的参数进行POST访问,这里我传了一个文件名为视图桌面(26).jpg的图片文件,传输形式为form-data。下面我们可以看到视图返回的信息有两个,一个是code代码执行成功,另一个是imgurl,这时返回给前端的图片地址,前端拿到这个地址再插入到想显示的地方即可。
前端访问
在tinymce富文本中上传图片测试下该功能,将http://127.0.0.1:8000/uploadimage/该接口引入前端,获取imgurl并将imgurl传给success,这样我们就可以在富文本中插入该图片了。
// 图片上传
images_upload_handler: function (blobInfo, success, failure){
let formData = new FormData()
console.log(blobInfo.filename())
formData.append('img',blobInfo.blob())
self.$axios.post('http://127.0.0.1:8000/uploadimage/',formData)
.then(response =>{
console.log(response.data['imgurl'])
if(response.data['code']==200){
success(response.data['imgurl'])
}else{
failure('上传失败!')
}
})
},
// 挂载的DOM对象
selector: `#${this.tinymceId}`,
})
选中富文本中的图片,在Element中我们可以看到对应的标签,在img中可以看到我们后台返回的图片地址。
你也可以拿着这个地址直接在浏览器中访问,直接在地址中输入即可访问该图片。
发现个小问题(好奇葩):
在tinymce中复制粘贴时发现的,当我直接复制图片然后在tinymce中粘贴时会提示上传失败,后台也没获取到图片,但是通过截图在粘贴就可以成功上传,如果通过图片查看器复制粘贴也能成功,但同时显示上传失败提示,后台有获取到对应的文件。看来tingymce的复制粘贴还有点小问题,需要重写了!
欢迎关注本人的公众号:编程手札,文章也会在公众号更新