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/

 

 



目录
相关文章
|
7月前
|
API 数据库 数据安全/隐私保护
利用Django框架构建高效后端API服务
本文将介绍如何利用Django框架构建高效的后端API服务。通过深入分析Django框架的特性和优势,结合实际案例,探讨了如何利用Django提供的强大功能来构建高性能、可扩展的后端服务。同时,还对Django框架在后端开发中的一些常见问题进行了解决方案的探讨,并提出了一些建设性的建议。
204 3
|
3月前
|
Python
django下载文件4-4|
django下载文件4-4|
|
4月前
|
JSON API 数据安全/隐私保护
哇塞!Django REST framework 太逆天啦!构建 API 服务从未如此轻松,你还不来试试?
【8月更文挑战第31天】Django REST framework(DRF)是基于Django框架的高效Web API开发工具,提供序列化、视图集、路由等功能,简化API构建流程。使用DRF可轻松实现数据的序列化与反序列化,并支持权限管理和认证机制以保障API安全。安装DRF只需通过`pip install djangorestframework`命令。要创建基本项目,先安装Django并创建新应用,定义模型、序列化器及视图集,最后配置路由。测试API时,可通过Postman发送HTTP请求验证功能。无论项目大小,DRF均能提供强大支持。
49 0
|
4月前
|
SQL Shell API
python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API
python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API
|
5月前
|
API 数据库 数据安全/隐私保护
Django配置api、管理系统和视图
Django配置api、管理系统和视图
133 1
|
4月前
|
中间件 API 网络架构
Django后端架构开发:从匿名用户API节流到REST自定义认证
Django后端架构开发:从匿名用户API节流到REST自定义认证
51 0
|
4月前
|
JSON API 网络架构
Django 后端架构开发:DRF 高可用API设计与核心源码剖析
Django 后端架构开发:DRF 高可用API设计与核心源码剖析
96 0
|
5月前
|
安全 API 网络安全
Django RESTful API安全实践
【7月更文挑战第19天】构建安全的Django RESTful API需要综合考虑多个方面,包括身份验证与授权、数据验证与清洗、安全的HTTPS连接、限制请求频率以及审计与日志记录等。通过实施这些安全实践,可以有效地保护API服务免受各种安全威胁的侵害,确保用户数据的安全性和服务的可用性。开发者在开发RESTful API时,应始终将安全性放在首位,确保API服务的安全可靠。
|
6月前
|
JSON 前端开发 API
Django API开发实战:前后端分离、Restful风格与DRF序列化器详解
Django API开发实战:前后端分离、Restful风格与DRF序列化器详解
|
API 开发工具 开发者
在钉钉中,服务端API 如何获取OA审批评论中的附件下载链接?
在钉钉中,服务端API 如何获取OA审批评论中的附件下载链接?
988 1