Django 实现文件上传下载API

简介: Django 实现文件上传下载API

Django 实现文件上传下载API


开发环境

 

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

附件表设计

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

fromdjango.db importmodels

 

# 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,添加以下带背景色部分的代码内容

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

#!/usr/bin/env python

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

 

__author__ ='授客'

 

fromdjango.contrib importadmin

fromdjango.urls importpath

 

fromdjango.conf.urls importinclude

 

 

urlpatterns =[

path('admin/', admin.site.urls),

path('', include('mywebsite.urls')) #添加API路由配置(这里根据项目实际情况配置)

]


 

项目settings.py配置

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

1

2

MEDIA_URL ='/media/'

MEDIA_ROOT =os.path.join(BASE_DIR, 'media').replace('\\', '/')

  

应用view视图编写

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

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

#!/usr/bin/env python

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

  

__author__ ='授客'

  

fromrest_framework.views importAPIView

fromrest_framework.response importResponse

fromrest_framework importstatus

from.models importAttachment

fromdjango.http importFileResponse

fromdjango.utils importtimezone

fromdjango.conf importsettings

importos

importuuid

  

  

importlogging

  

logger =logging.getLogger('mylogger')

  

# 批量创建目录

defmkdirs_in_batch(path):

    try:

        path =os.path.normpath(path)  # 去掉路径最右侧的 \\ 、/

        path =path.replace('\\', '/') # 将所有的\\转为/,避免出现转义字符串

        head, tail =os.path.split(path)

        ifnotos.path.isdir(path) andos.path.isfile(path):  # 如果path指向的是文件,则分解文件所在目录

            head, tail =os.path.split(head)

 

        iftail =='': # head为根目录,形如 / 、D:

            returnTrue

 

        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

                ifnotos.path.isdir(dir_path):

                    os.mkdir(dir_path)

                head, tail =os.path.split(head)

        returnTrue

    exceptException as e:

        logger.error('批量创建目录出错:%s'%e)

        returnFalse

 

  

classAttachmentAPIView(APIView):

# 上传附件

defpost(self, request, format=None):

    result ={}

    try:

        files =request.FILES

        file=files.get('file')

 

        ifnotfile:

            result['msg'='上传失败,未获取到文件'

            result['success'=False

            returnResponse(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

        ifnotos.path.exists(file_absolute_path):# 路径不存在

            ifnotutils.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:

            forchunk infile.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);

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

# 删除附件

defdelete(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) andos.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)

 


 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

# 下载附件

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) andos.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,文件内容如下:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

#!/usr/bin/env python

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

 

__author__ ='授客'

 

fromdjango.urls importre_path

 

from.views importAttachmentAPIView

 

 

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/

 

 



目录
相关文章
|
2月前
|
API 数据库 数据安全/隐私保护
利用Django框架构建高效后端API服务
本文将介绍如何利用Django框架构建高效的后端API服务。通过深入分析Django框架的特性和优势,结合实际案例,探讨了如何利用Django提供的强大功能来构建高性能、可扩展的后端服务。同时,还对Django框架在后端开发中的一些常见问题进行了解决方案的探讨,并提出了一些建设性的建议。
52 3
|
Java API
JDK API文档中文版(1.6、1.8、1.9)(附百度网盘下载地址)
JDK API文档中文版(1.6、1.8、1.9)(附百度网盘下载地址)
1714 0
JDK API文档中文版(1.6、1.8、1.9)(附百度网盘下载地址)
|
18天前
|
前端开发 API 数据库
Django(五):如何在Django中通过API提供数据库数据给前端
Django(五):如何在Django中通过API提供数据库数据给前端
|
5月前
|
算法 关系型数据库 API
Python【算法中心 02】Web框架Django管理页面使用(管理员账号创建+API使用+应用添加)GreenPlum数据库引擎及API测试
Python【算法中心 02】Web框架Django管理页面使用(管理员账号创建+API使用+应用添加)GreenPlum数据库引擎及API测试
43 0
|
10月前
|
JSON API 数据安全/隐私保护
【Django学习】(十五)API接口文档平台_项目流程分析_日志器_认证_授权
【Django学习】(十五)API接口文档平台_项目流程分析_日志器_认证_授权
|
JavaScript 中间件 API
Django 解决跨域访问API失败问题
Django 解决跨域访问API失败问题
123 0
|
移动开发 监控 小程序
Python3.7配合Django2.0来调用钉钉(dingding)在线api实时监测员工考勤打卡情况
新冠疫情期间,大多数公司为了避免交叉感染都或多或少的采用了远程办公的方式,这显然是一个明智的选择,基本上钉钉(dingding)作为一个远程办公平台来用的话,虽然差强人意,但是奈何市面上没有啥更好的选择,矬子里拔将军,也还是可以凑合用的,不过远程办公有个问题,就是每天需要检查员工的考勤,居家办公虽然灵活,但是大家究竟有没有办公,则是另外一回事,钉钉提供的解决方案就是考勤在线打卡功能,但是检查出勤钉钉在移动端就有点费劲,需要在钉钉app里点击至少5次,还不能实时刷新,pc端的钉钉oa系统做的更烂,还不如移动端来得方便,另外如果你在一家上千人的企业里,这家企业有大大小小几十个部门,你又非常倒霉的担
Python3.7配合Django2.0来调用钉钉(dingding)在线api实时监测员工考勤打卡情况
|
存储 前端开发 JavaScript
使用python3.7+Vue.js2.0+Django2.0.4异步前端通过api上传文件到七牛云云端存储
之前一篇文章是通过普通js+tornado来上传七牛云:[使用Tornado配合七牛云存储api来异步切分上传文件](https://v3u.cn/a_id_123),本次使用vue+django来进行异步上传,因为毕竟vue.js才是目前的前端的主流。
使用python3.7+Vue.js2.0+Django2.0.4异步前端通过api上传文件到七牛云云端存储
|
SQL 数据库 Python
Django ORM Model的基本查询操作API
1.创建Model实例对象 阅读文本前请参考此文章的数据表结构 使用save方法创建Model实例: 由于Topic需要一个User对象,所以,先获取username是admin(超级用户)的User对象,再去创建Topic对象
43871 1
Django ORM Model的基本查询操作API