Django 实现文件上传下载API
Django 实现文件上传下载API
by:授客 QQ:1033553122 欢迎加入全国软件测试交流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的更多相关文章
- django 12天(跨域,文件上传,下载,cookie,session)
django 12天(跨域,文件上传,下载) 跨域 什么是跨域 1.协议不同 2.端口不同 3.主机不同 如何解决跨域 1.安装django-cors-headers模块 2.在settings.py ...
- Django文件上传下载与富文本编辑框
django文件上传下载 上传 配置settings.py # 设定文件的访问路径,如:访问http://127.0.0.1:8000/media/就可以获取文件 MEDIA_URL = '/medi ...
- java web 文件上传下载
文件上传下载案例: 首先是此案例工程的目录结构:
- 文件上传下载样式 --- bootstrap
在平时工作中,文件上传下载功能属于不可或缺的一部分.bootstrap前端样式框架也使用的比较多,现在根据bootstrap强大的样式模板,自定义一种文件下载的样式. 后续会使用spring MVC框 ...
- .Net Core 图片文件上传下载
当下.Net Core项目可是如雨后春笋一般发展起来,作为.Net大军中的一员,我热忱地拥抱了.Net Core并且积极使用其进行业务的开发,我们先介绍下.Net Core项目下实现文件上传下载接口. ...
- Java实现FTP批量大文件上传下载篇1
本文介绍了在Java中,如何使用Java现有的可用的库来编写FTP客户端代码,并开发成Applet控件,做成基于Web的批量.大文件的上传下载控件.文章在比较了一系列FTP客户库的基础上,就其中一个比 ...
- jm解决乱码问题-参数化-数据库操作-文件上传下载
jm解决乱码问题-参数化-数据库操作-文件上传下载 如果JM出果运行结果是乱码(解决中文BODY乱码的问题) 找到JM的安装路径,例如:C:\apache-jmeter-3.1\bin 用UE打开jm ...
- SpringMVC整合fastdfs-client-java实现web文件上传下载
原文:http://blog.csdn.net/wlwlwlwl015/article/details/52682153 本篇blog主要记录一下SpringMVC整合FastDFS的Java客户端实 ...
- javaEE(14)_文件上传下载
一.文件上传概述 1.实现web开发中的文件上传功能,需完成如下二步操作: •在web页面中添加上传输入项•在servlet中读取上传文件的数据,并保存到本地硬盘中. 2.如何在web页面中添加上传输 ...
- 使用Typescript重构axios(二十五)——文件上传下载进度监控
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
随机推荐
- aspnetcore插件开发dll热加载 二
这一篇文章应该是个总结. 投简历的时候是不是有人问我有没有abp的开发经历,汗颜! 在各位大神的尝试及自己的总结下,还是实现了业务和主机服务分离,通过dll动态的加载卸载,控制器动态的删除添加. 项目 ...
- 在kubernetes里使用AppArmor限制容器对资源的访问
目录 一.系统环境 二.前言 三.AppArmor简介 四.AppArmor和SELinux的区别 五.使用AppArmor限制nginx程序访问目录 5.1 安装nginx 5.2 修改nginx的 ...
- Symbol.for()
当我们在不同的模块或文件中需要共享一个特定的Symbol时,可以使用Symbol.for()方法来实现. 假设我们有两个模块,分别是module1.js和module2.js.我们希望在这两个模块中使 ...
- mysql windows 下配置可远程连接
1.在防火墙入站规则里加入 3306 端口,3306 为你安装mysql 时的端口. 2.在mysql 命令行中输入: #应用mysql数据库use mysql;#将root用户可访问改成所有upd ...
- Mysql int类型字段插入表达式,值为0或1
CREATE TABLE `t_user` ( `uId` INT(11) DEFAULT NULL, `uName` VARCHAR(20) DEFAULT NULL, `uPwd` VARCHAR ...
- SM4Utils加解密demo
SM4Utils加解密demo package com.example.core.mydemo.sm4; import cn.org.bjca.utils.SM4Utils; public class ...
- Imdeploy笔记
Smiling & Weeping ---- 天气不好的时候,我会小心地把自己心上的裂缝补起来.为什么?... LMDeploy 的量化和部署 1 环境配置 2 服务部署 2.1 模型转换 2 ...
- Flutter 借助SearchDelegate实现搜索页面,实现搜索建议、搜索结果,解决IOS拼音问题
搜索界面使用Flutter自带的SearchDelegate组件实现,通过魔改实现如下效果: 搜素建议 搜索结果,支持刷新和加载更多 IOS中文输入拼音问题 界面预览 拷贝源码 将SearchDele ...
- FFmpeg开发笔记(三十二)利用RTMP协议构建电脑与手机的直播Demo
不管是传统互联网还是移动互联网,实时数据传输都是刚需,比如以QQ.微信为代表的即时通信工具,能够实时传输文本和图片.其中一对一的图文通信叫做私聊,多对多的图文通信叫做群聊. 除了常见的图文即时通信,还 ...
- 解读MySQL 8.0数据字典的初始化与启动
本文分享自华为云社区<MySQL全文索引源码剖析之Insert语句执行过程>,作者:GaussDB 数据库. 本文主要介绍MySQL 8.0数据字典的基本概念和数据字典的初始化与启动加载的 ...