Django 实现文件上传下载API

Django 实现文件上传下载API[Python基础]

Django 实现文件上传下载API

by:授客 QQ1033553122 欢迎加入全国软件测试交流QQ群

开发环境

 

Win 10

 

Python 3.5.4

 

Django-2.0.13.tar.gz

官方下载地址:

https://www.djangoproject.com/download/2.0.13/tarball/

 

vue 2.5.2

 

djangorestframework-3.9.4

下载地址:

https://github.com/encode/django-rest-framework

附件表设计

fromdjango.dbimport models
 
# Create your models here.
 
 
# 附件表
classAttachment(models.Model):
    id = models.AutoField(primary_key=True, verbose_name="自增id")
    name = models.CharField(max_length=200, verbose_name="附件名称")
file_path = models.CharField(max_length=200, verbose_name="附件相对路径")
create_time =  models.DateTimeField(verbose_name="上传时间")
 
classMeta:
db_table = "tb_attachment"
verbose_name = "附件表"
verbose_name_plural = verbose_name

 

项目urls.py配置

修改项目根目录下的urls.py,添加以下带背景色部分的代码内容
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
__author__ = "授客"
 
fromdjango.contribimport admin
fromdjango.urlsimport path
 
fromdjango.conf.urlsimport include
 
 
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("mywebsite.urls"))
]

 

项目settings.py配置

在文件末尾添加以下配置,用于存放附件

MEDIA_URL = “/media/”

MEDIA_ROOT = os.path.join(BASE_DIR, “media”).replace(“”, “/”)

 

应用view视图编写

例中直接在views.py视图编写视图,代码如下

 

 

#!/usr/bin/env python

# -*- coding:utf-8 -*-

 

__author__ = 授客

 

from rest_framework.views import APIView

from rest_framework.response import Response

from rest_framework import status

from .models import Attachment

from django.http import FileResponse

from django.utils import timezone

from django.conf import settings

import os

import uuid

 

 

importlogging

 

logger = logging.getLogger(“mylogger”)

 

 
# 批量创建目录
defmkdirs_in_batch(path):
try:
        path = os.path.normpath(path)  # 去掉路径最右侧的  /
path = path.replace("", "/") # 将所有的转为/,避免出现转义字符串
head, tail = os.path.split(path)
if not os.path.isdir(path) and os.path.isfile(path):  # 如果path指向的是文件,则分解文件所在目录
head, tail = os.path.split(head)
 
if tail == "": # head为根目录,形如 / D:
return True
 
new_dir_path = ""  # 存放反转后的目录路径
root = ""  # 存放根目录
whiletail:
new_dir_path = new_dir_path + tail + "/"
head, tail = os.path.split(head)
root = head
else:
new_dir_path = root + new_dir_path
 
# 批量创建目录
new_dir_path = os.path.normpath(new_dir_path)
head, tail = os.path.split(new_dir_path)
temp = ""
whiletail:
temp = temp + "/" + tail
dir_path = root + temp
if not os.path.isdir(dir_path):
os.mkdir(dir_path)
head, tail = os.path.split(head)
return True
exceptException as e:
logger.error("批量创建目录出错:%s" % e)
return False

 

 

 

 
classAttachmentAPIView(APIView):

# 上传附件

defpost(self, request, format=None):

result = {}

try:

files = request.FILES

file = files.get(“file”)

 

if not file:

                result[“msg”] =  上传失败,未获取到文件

result[“success”] =  False

returnResponse(result, status.HTTP_400_BAD_REQUEST)

 

   # data = request.POST #获取前端发送的,file之外的其它参数

# extra = data.get(“extr”)

file_name = file.name

attachment_name = file_name

creater = request.user.username

create_time = timezone.now()

time_str = create_time.strftime(“%Y%m%d”)

name, suffix = os.path.splitext(file_name)

file_name = str(uuid.uuid1()).replace(“-“, “”) + time_str + suffix

file_relative_path = “/myapp/attachments/”+ time_str

file_absolute_path = settings.MEDIA_ROOT + file_relative_path

if not os.path.exists(file_absolute_path):# 路径不存在

if not utils.mkdirs_in_batch(file_absolute_path):

                    result[“msg”] =  批量创建路径(%s)对应的目录失败% file_absolute_path

result[“success”] =  False

returnResponse(result, status.HTTP_500_INTERNAL_SERVER_ERROR)

file_relative_path += “/” + file_name

data[“file_path”] = file_relative_path

 

file_absolute_path = file_absolute_path + “/” + file_name

file_handler = open(file_absolute_path, “wb”)    # 打开特定的文件进行二进制的写操作

 

try:

for chunk in file.chunks():      # 分块写入文件

file_handler.write(chunk)

finally:

file_handler.close()

# 记录到数据库

try:

obj = Attachment(file_path=file_path, name=attachment_name, create_time=create_time, creater=creater)

obj.save()

exceptException as e:

                result[“msg”] =  上传失败:%s” % e

result[“success”] =  False

returnResponse(result, status.HTTP_400_BAD_REQUEST)

 

result[“msg”] =  上传成功

result[“success”] =  True

result[“data”] =  result_data

returnResponse(result, status.HTTP_200_OK)

exceptException as e:

result[“msg”] =  “%s” % e

result[“success”] =  False

returnResponse(result, status.HTTP_500_INTERNAL_SERVER_ERROR)

 

注意:这里采用UploadedFile.chunks()分块写入,而不是直接使用UploadedFile.read()一次性读取整个文件,是因为如果文件比较大,一次性读取过多内容,会占用系统过多的内存,进而让系统变得更低效。默认的chunks分块默认值为2.5M

 

file = files.get(“file”)# 注意:这里的字典key“file”要和前端提交form表单请求时,文件对象对应的表单key保持一致,前端代码如下

letform = newFormData();

form.append(“file”, file);

 

 

 

# 删除附件

def delete(self, request, format=None):

result = {}

try:

data = request.data

attachment_id = data.get(“attachment_id”)

obj = Attachment.objects.filter(id=attachment_id).first()

ifobj:

file_absoulte_path = settings.MEDIA_ROOT + “/”+ obj.file_path

ifos.path.exists(file_absoulte_path) and os.path.isfile(file_absoulte_path):

os.remove(file_absoulte_path)

obj.delete()

result[“msg”] =  删除成功

result[“success”] =  True

returnResponse(result, status.HTTP_200_OK)

exceptException as e:

result[“msg”] =  “%s” % e

result[“success”] =  False

returnResponse(result, status.HTTP_500_INTERNAL_SERVER_ERROR)

 

# 下载附件

defget(self, request, format=None):

result = {}

try:

data = request.GET

attachment_id = data.get(“attachmentId”)

obj = Attachment.objects.filter(id=attachment_id).first()

ifobj:

file_absoulte_path = settings.MEDIA_ROOT+  obj.file_path

ifos.path.exists(file_absoulte_path) and os.path.isfile(file_absoulte_path):

file = open(file_absoulte_path, “rb”)

file_response = FileResponse(file)

file_response[“Content-Type”]=“application/octet-stream”

file_response[“Access-Control-Expose-Headers”] = “Content-Disposition” # 设置可以作为响应的一部分暴露给外部的请求头,如果缺少这行代码,会导致前端请求响应中看不到该请求头

file_response[“Content-Disposition”]=“attachment;filename={}”.format(urlquote(obj.name)) # 这里使用urlquote函数主要为针对文件名为中文时,对文件名进行编码,编码后,前端获取的文件名称形如%E5%AF%BC%E5%87%BA%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B

returnfile_response

else:

result[“msg”] =  请求失败,资源不存在

result[“success”] =  False

else:

result[“msg”] =  请求失败,资源不存在

result[“success”] =  False

returnResponse(result, status.HTTP_200_OK)

exceptException as e:

result[“msg”] =  “%s” % e

result[“success”] =  False

returnResponse(result, status.HTTP_500_INTERNAL_SERVER_ERROR)

 

 

说明:

file_response = FileResponse(file),可以在引入StreamingHttpResponse之后(from django.http import StreamingHttpResponse),替换为

file_response = StreamingHttpResponse(file)

 

 

前端获取响应头中文件名方法如下:

let disposition = res.headers[“content-disposition”];

let filename = decodeURI(disposition.replace(“attachment;filename=”, “”) );

 

# do something,比如下载:

link.setAttribute(“download”, filename);

 

应用urls.py配置

新建urls.py,文件内容如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
__author__ = "授客"
 
from django.urlsimport re_path
 
from.views import AttachmentAPIView
 
 
urlpatterns = [
...
re_path("^api/v1/testcase/d+/attachment$", testcase_attachment_views.TestcaseAttachmentAPIView.as_view()), # 给测试用例添加附件
re_path("^api/v1/testcase/d+/attachment/d+$", testcase_attachment_views.TestcaseAttachmentAPIView.as_view()), # 删除、下载测试用例关联的附件

]

 

前端实现

 

参考链接

https://docs.djangoproject.com/zh-hans/2.1/topics/http/file-uploads/

https://docs.djangoproject.com/zh-hans/2.0/ref/files/uploads/

 

 

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » Django 实现文件上传下载API