Django 实现文件上传下载API

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

开发环境

 

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

附件表设计


from django.db import models

# Create your models here.

# 上传文件表
class Attachment(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__ = '授客' from django.contrib import admin
from django.urls import path from django.conf.urls import include urlpatterns = [
path('admin/', admin.site.urls),
path('', include('mywebsite.urls')) #添加API路由配置(这里根据项目实际情况配置)
]

  

项目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 import logging logger = logging.getLogger('mylogger') # 批量创建目录
def mkdirs_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 = '' # 存放根目录
while tail:
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 = ''
while tail:
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
except Exception as e:
logger.error('批量创建目录出错:%s' % e)
return False class AttachmentAPIView(APIView):
# 上传附件
def post(self, request, format=None):
result = {}
try:
files = request.FILES
file = files.get('file') if not file:
result['msg'] = '上传失败,未获取到文件'
result['success'] = False
return Response(result, status.HTTP_400_BAD_REQUEST) # data = request.POST #获取前端发送的,file之外的其它参数
# extra = data.get('extra')
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
return Response(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()
except Exception as e:
result['msg'] = '上传失败:%s' % e
result['success'] = False
return Response(result, status.HTTP_400_BAD_REQUEST) result['msg'] = '上传成功'
result['success'] = True
result['data'] = result_data
return Response(result, status.HTTP_200_OK)
except Exception as e:
result['msg'] = '%s' % e
result['success'] = False
return Response(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()
if obj:
file_absoulte_path = settings.MEDIA_ROOT + '/'+ obj.file_path
if os.path.exists(file_absoulte_path) and os.path.isfile(file_absoulte_path):
os.remove(file_absoulte_path)
obj.delete()
result['msg'] = '删除成功'
result['success'] = True
return Response(result, status.HTTP_200_OK)
except Exception as e:
result['msg'] = '%s' % e
result['success'] = False
return Response(result, status.HTTP_500_INTERNAL_SERVER_ERROR)

  

    # 下载附件
def get(self, request, format=None):
result = {}
try:
data = request.GET
attachment_id = data.get('attachmentId')
obj = Attachment.objects.filter(id=attachment_id).first()
if obj:
file_absoulte_path = settings.MEDIA_ROOT+ obj.file_path
if os.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”
return file_response
else:
result['msg'] = '请求失败,资源不存在'
result['success'] = False
else:
result['msg'] = '请求失败,资源不存在'
result['success'] = False
return Response(result, status.HTTP_200_OK)
except Exception as e:
result['msg'] = '%s' % e
result['success'] = False
return Response(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.urls import 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()), # 删除、下载测试用例关联的附件

  

前端实现

参考文档“ElementUI Upload上传(利用http-request自定义上传)&下载&删除附件”

参考链接

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

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

Django 实现文件上传下载API的更多相关文章

  1. django 12天(跨域,文件上传,下载,cookie,session)

    django 12天(跨域,文件上传,下载) 跨域 什么是跨域 1.协议不同 2.端口不同 3.主机不同 如何解决跨域 1.安装django-cors-headers模块 2.在settings.py ...

  2. Django文件上传下载与富文本编辑框

    django文件上传下载 上传 配置settings.py # 设定文件的访问路径,如:访问http://127.0.0.1:8000/media/就可以获取文件 MEDIA_URL = '/medi ...

  3. java web 文件上传下载

    文件上传下载案例: 首先是此案例工程的目录结构:

  4. 文件上传下载样式 --- bootstrap

    在平时工作中,文件上传下载功能属于不可或缺的一部分.bootstrap前端样式框架也使用的比较多,现在根据bootstrap强大的样式模板,自定义一种文件下载的样式. 后续会使用spring MVC框 ...

  5. .Net Core 图片文件上传下载

    当下.Net Core项目可是如雨后春笋一般发展起来,作为.Net大军中的一员,我热忱地拥抱了.Net Core并且积极使用其进行业务的开发,我们先介绍下.Net Core项目下实现文件上传下载接口. ...

  6. Java实现FTP批量大文件上传下载篇1

    本文介绍了在Java中,如何使用Java现有的可用的库来编写FTP客户端代码,并开发成Applet控件,做成基于Web的批量.大文件的上传下载控件.文章在比较了一系列FTP客户库的基础上,就其中一个比 ...

  7. jm解决乱码问题-参数化-数据库操作-文件上传下载

    jm解决乱码问题-参数化-数据库操作-文件上传下载 如果JM出果运行结果是乱码(解决中文BODY乱码的问题) 找到JM的安装路径,例如:C:\apache-jmeter-3.1\bin 用UE打开jm ...

  8. SpringMVC整合fastdfs-client-java实现web文件上传下载

    原文:http://blog.csdn.net/wlwlwlwl015/article/details/52682153 本篇blog主要记录一下SpringMVC整合FastDFS的Java客户端实 ...

  9. javaEE(14)_文件上传下载

    一.文件上传概述 1.实现web开发中的文件上传功能,需完成如下二步操作: •在web页面中添加上传输入项•在servlet中读取上传文件的数据,并保存到本地硬盘中. 2.如何在web页面中添加上传输 ...

  10. 使用Typescript重构axios(二十五)——文件上传下载进度监控

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

随机推荐

  1. wblockCloneObjects 写块克隆的使用

    写块克隆可以把当前数据库的实体写入到另一个dwg文件中去.用法根deepclone类似,不过deepclone只能复制到同一数据库中,而写块克隆是在不同数据库中进行复制的.写块克隆也算是深度克隆,能把 ...

  2. Swoole 源码分析之 Http Server 模块

    首发原文链接:Swoole 源码分析之 Http Server 模块 Swoole 源码分析之 Http Server 模块 Http 模块的注册初始化 这次我们分析的就是 Swoole 官网的这段代 ...

  3. mysql存储地理信息的方法

    MySQL 存储地理信息通常使用 GEOMETRY 数据类型或其子类型(如 POINT, LINESTRING, POLYGON 等).为了支持这些数据类型,MySQL 提供了 SPATIAL 索引, ...

  4. SQLServer如何监控阻塞会话

    一.查询阻塞和被阻塞的会话 SELECT r.session_id AS [Blocked Session ID], r.blocking_session_id AS [Blocking Sessio ...

  5. nginx aio模块添加与配置

    1. 升级目的 让现有服务平滑过渡到高版本,减少服务漏洞,提高服务性能 让其支持nginx最新特性 nginx threads模块 2. 获取nginx1.7.2版本 wget http://ngin ...

  6. 【Java编程教程】详解Java 中的对象和类

    在本页中,我们将了解 Java 对象和类.在面向对象的编程技术中,我们使用对象和类来设计程序. Java中的对象既是物理实体又是逻辑实体,而Java中的类只是逻辑实体. 什么是Java中的对象 具有状 ...

  7. springboot项目配置多数据源

    springboot项目配置多数据源 //关键:mybatis文件的目录需要区分开来 sqlSessionFactoryBean.setMapperLocations(new PathMatching ...

  8. SQL索引优化,菜单列表优化

    SQL索引优化,菜单列表优化 现象:在系统中几个数据量大的列表页面,首次进入页面未增加筛选条件,导致进入的列表查询速度非常慢.分析:通过SQL查看,是做了count求和查询,然后根据总的记录数来做分页 ...

  9. 警告: BASE64Decoder是内部专用 API, 可能会在未来发行版中删除

    警告: BASE64Decoder是内部专用 API, 可能会在未来发行版中删除 import org.apache.commons.codec.binary.Base64; public class ...

  10. 容器镜像安全:安全漏洞扫描神器Trivy

    目录 一.系统环境 二.前言 三.Trivy简介 四.Trivy漏洞扫描原理 五.利用trivy检测容器镜像的安全性 六.总结 一.系统环境 本文主要基于Docker version 20.10.14 ...