说在前面
写本文章最初是 为了由于熟练度低 日后回顾知识时提供帮助 所以本篇文章意在对其中涉及到的大多数知识点做汇总和讲解,尽可能细致 但由于本人水平有限 所以可能存在一些地方的理解是错误的 欢迎指正。主要功能便是 去实现单个或多个文件的上传与下载,以及对后端任务进行监听以实现随时中断。
Vue部分
1.1外部结构
在router下面的index.js中定义了路由类似django,即输入基地址便会重定向到BASE/index,而与/index便会触发import()中的内容作为响应
在xxx.vue中类似这种结构
<script> import md5 from 'js-md5' export default { name: 'login', data(){ message1: '', }, methods:{ }, } </script>
<template>使用element组件去定义html</template>
<style>即css</style>
其中data中定义数据,这些数据类似全局变量 但要this.变量名进行调用,methods中定义函数,
1.2handleFileUpload1函数部分
<input type="file" @change="handleFileUpload1" class="custom-file-input"> <input type="file" @change="handleFileUpload1" class="custom-file-input"> <el-button @click="uploadFile1" class="primary-button">上传</el-button> <el-button v-if="areadydouwnload1" @click="download1" class="secondary-button">点击下载</el-button>
@change:监听标签,如果内部数据发生变化便会触发函数handleFileUpload1,,注意@change放的位置 是监听的标签上面,而不是 form-item标签上面
handleFileUpload1(event) { console.log(event) console.log(typeof (this.file11)) this.file1.push(event.target.files[0]); console.log(this.file1) if (this.file1.length > 2) { this.file1.splice(0, this.file1.length); this.message1 = '选择了多个文件,请重新选择' } else { this.message1 = '' } },
event 参数是一个事件对象,它包含了关于触发事件的所有信息,比如事件类型、触发事件的目标元素等
你再往下滑,会看到target中的files对象中包含了文件信息
event.target: 触发事件的 DOM 元素,这里是文件输入控件。
event.target.files: 一个 FileList 对象,包含了用户选择的所有文件。可以通过索引访问这些文件,例如 event.target.files[0] 获取第一个文件
this.file1.push(event.target.files[0]); 此处是因为JavaScript 数组提供了类似栈的操作,但其本身仍是数组,向数组尾部压入数据,相关函数还有pop弹出数组尾部数据。
此外JavaScript 数组还有函数array.splice(start, deleteCount, item1, item2, ...)
start: 开始删除的位置的索引。
deleteCount: 要删除的元素的数量。
item1, item2, ...: (可选)要添加到数组的新元素。
1.3uploadFile1函数部分
async uploadFile1() {
if (this.file1.length == 0) {
this.message1 = '请选择一个文件';
return;
}
console.log(this.file1)
console.log(this.csrfToken)
const formData = new FormData();
// formData.push('file', this.file1, this.file1.name);
for (let i = 0; i < this.file1.length; i++) {
formData.append('files', this.file1[i], this.file1[i].name);
}
console.log(formData)
try {
this.open1=false;
const response = await fetch('http://xxxxx:8003/index_child1', {
method: 'POST',
body: formData
});
console.log(typeof (response));
console.log(response);
if (response.status == 200) {
this.dblob1 = await response.blob();
this.durl1 = window.URL.createObjectURL(this.dblob1);
this.areadydouwnload1 = true
} else {
throw new Error('文件上传失败');
}
} catch (error) {
// console.error('文件上传出错:', error);
alert('文件上传出错', error);
this.open1=true;
// 处理错误逻辑
}
},
async download1() {
const link = document.createElement('a');
link.href = this.durl1;
link.setAttribute('download', 'processed_data.csv'); // 更改文件名为你需要的
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(this.durl1);
this.areadydouwnload1 = false
this.clearFileInput1()
this.open1=true
},
1.3.1 formdata
FormData 对象是 HTML5 引入的一个特性,它允许你轻松地构建一个键值对集合,通常用于通过 AJAX 请求发送表单数据。它可以自动处理文件上传,无需手动编码 multipart/form-data 格式
一旦创建了 FormData 对象,你可以使用以下方法来操作它:
append(key, value):
用于向 FormData 对象中添加键值对。
如果键已经存在,则新的值会被追加到现有的值后面。
delete(key):
用于删除 FormData 对象中的某个键及其对应的值。
get(key):
获取 FormData 对象中指定键的第一个值。
getAll(key):
获取 FormData 对象中指定键的所有值。
has(key):
判断 FormData 对象中是否存在指定的键。
set(key, value):
设置 FormData 对象中指定键的值。如果键已经存在,则替换原来的值。
entries(), keys(), values():
分别返回键值对、键和值的迭代器
此处对append函数做额外说明:
formData.append(name, value[, filename]);
name:是要添加的键名(字符串)。
value:是要添加的值,可以是 Blob 或 String 类型。
filename(可选):当 value 是 Blob 类型时,用于指定文件的名称(字符串)。这仅在上传文件时有用。即文件名
1.3.2 async与await
关于为何设置async,是因为函数内发送了网络请求以及请求后的一些处理,,async和await是成对出现的,去实现同步操作,如果此处没有async代码流程便变为:发出请求后直接对response.status进行判断,可此时response.status为未定义会直接导致报错,+上await之后,走到await处便会,类似挂起状态,知道接收到响应为止,程序执行流才会继续往下
1.3.3 fetch
fetch(url[, init])即为 fetch(url,dict) 其中init={header,method,body}即请求头请求体等等
fetch 返回的 Promise 解析为一个 Response 对象,提供了多种方法来处理响应数据:
json(): 解析响应体为 JSON。
text(): 解析响应体为文本。
blob(): 解析响应体为 Blob。
arrayBuffer(): 解析响应体为 ArrayBuffer。
status: HTTP 响应状态码。
statusText: HTTP 响应状态文本。
headers: HTTP 响应头。
ok: 布尔值,表示响应状态是否在 200-299 范围内。
此处重点说下bolb函数:Blob(Binary Large Object)即巨大的二进制对象,可以用来表示任何类型的文件,如图像、视频、音频文件等。
调用response.bolb会把响应体解析为二进制对象的方式接收
代码中干的事情就是,本地变量存储blob对象 再赋值给一个url,然后创建一个a标签 把他的link设置为该url并且设置为下载类型,文件名为...。
1.4 uploadFile5
async uploadFile5() {
if (this.file5.length == 0) {
this.message5 = '请选择一个文件';
return;
}
const formData = new FormData();
formData.append('file', this.file5[0], this.file5[0].name);
console.log(formData)
this.open5=false;
await fetch('http://xxxxxx:8003/index_child5', {
method: 'POST',
body: formData,
})
.then(response => response.json())
.then(data => {
this.processId = data.process_id;
});
},
这个函数是实现了,上传文件进行爬虫程序运行,然后监听程序运行以实现任意中断
.then(response => response.json())
.then(data => {
this.processId = data.process_id;
});
等价于
.then(response => {return response.json()})
.then(data => { // 此处data接收了上个then的返回值 可以任意命名
this.processId = data.process_id;
});
promise.then(onFulfilled, onRejected); promise被解析时调用onFulfilled被拒绝时调用onRejected
then 方法的行为
链式调用:
您可以在多个 .then 方法中链式调用,以便按顺序处理结果。
每个 .then 方法都可以返回一个新的 Promise,这样下一个 .then 方法将等待前一个 Promise 完成后再执行。
返回值:
您可以在 .then 的回调函数中返回值,这个值将成为下一个 .then 方法的参数。
如果返回一个 Promise,那么下一个 .then 方法将在该 Promise 解析后被调用。
错误处理:
如果在 .then 的回调函数中抛出异常,那么该异常将被传递给下一个 .catch 方法。
如果没有提供 .catch 方法,异常将被抛出
checkStatus() {
fetch(`http://xxxx:8003/check_status/${this.processId}/`)
${....}js的模板语法,实现字符串格式化,把进程的pid传输到后端检查进程的运行状态
2.1 后端处理
在Django中,request
对象代表了客户端发送的HTTP请求
request.method:获取请求方法(GET, POST, PUT, DELETE 等)。
request.GET:获取GET请求中的查询字符串参数。
request.POST:获取POST请求中的表单数据。
request.body:获取原始请求体数据(通常用于处理非表单数据或自定义数据格式)。
request.FILES:获取上传的文件(用于处理文件上传)。
request.COOKIES:获取客户端发送的Cookie。
request.META:获取请求元数据,包括HTTP头等。
request.user:获取当前认证用户对象(如果已登录)。
request.session:获取会话数据
requests.FILES
此处重点讲下request.FILES:一个 MultiValueDict
对象,它类似于字典,但允许一个键对应多个值。这使得它非常适合处理文件上传,因为一个表单字段可能会上传多个文件,包含所有通过 POST 请求上传的文件。request.FILES
是一个类似于字典的对象,键是文件字段的名称,值是 UploadedFile
对象。
UploadedFile
对象有以下几个重要属性和方法:
read:读取文件内容。可以指定读取的字节数,也可以读取整个文件。
file:UploadedFile
对象内部的实际文件对象。这个属性是一个 io.BufferedReader
实例,可以用来读取文件内容。
chunk:以块的形式读取文件内容。适合处理大文件,避免一次性读取大量数据
size
:上传文件的大小,以字节为单位
content_type
:文件的 MIME 类型(例如 image/jpeg
)。
request.FILES
的键是表单中 input
元素的 name
属性的值,也是formdata使用apend函数设置的键名
<!-- HTML 表单 -->
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="file">
<input type="submit" value="Upload">
</form>
如果一个表单字段允许上传多个文件(<input type="file" name="files" multiple>)
formData.append('files', this.file1[i], this.file1[i].name);
由于MultiValueDict
是一个类字典对象,所以可以使用 request.FILES['name']
来获取文件数据,name
是表单中文件输入字段的 name
属性值。
if 'files' not in request.FILES:
return JsonResponse({'error': 'xxxxx'}, status=400)
files = request.FILES.getlist('files')
df_list = []
for file in files:
print(file.name)
print(file)
# print(file.read())
content = file.read()
if file.name.endswith('.xlsx') or file.name.endswith('.xls'):
# 读取 Excel 文件
excel_file = pd.ExcelFile(BytesIO(content))
df = excel_file.parse(excel_file.sheet_names[0])
# df几位 dataframe类型数据
# print(df)
df_list.append(df)
pandas.ExcelFile
此处使用pd.ExcelFile函数的方式对文件进行处理
pandas.ExcelFile
用于打开一个 Excel 文件并提供对该文件内容的访问。它主要用于在读取 Excel 文件时,提供更细粒度的控制,特别是在处理多个工作表或大文件时
sheet_names
功能:返回 Excel 文件中所有工作表的名称。
parse(sheet_name)
功能:读取指定名称的工作表,并将其加载为 DataFrame。
read_excel()
功能:直接从 Excel 文件中读取数据,并将其加载为 DataFrame。等同于 pd.read_excel
的简化版本,pd.read_excel
可以接受更多参数
df = pd.read_excel('path/to/your/file.xlsx', sheet_name='Sheet1')
print(df.head())
book
功能:获取 Excel 文件的 workbook 对象。这在直接与 Excel 文件交互时很有用
workbook = excel_file.book
BytesIO
BytesIO:
用于在内存中操作字节流。它类似于文件对象,但数据不是从磁盘读取或写入,而是直接在内存中进行读写操作
buffer = BytesIO(b'initial data') 创建一个 BytesIO
对象
网络请求的响应
关于对客户端网络请求的响应,所要求的格式:
HttpResponse
的 content即响应的内容
主要要求是字节流(bytes
)或可以通过字符串(str
)转换为字节流的数据。json便是转换成类字符串类型然后再转换成字节流即比特流,基本上,你需要确保 content
能够转换为二进制数据,因为 HTTP 响应体最终会被发送为字节流。
response = HttpResponse(output.getvalue(), content_type='text/csv')
# print(output.getvalue())
response['Content-Disposition'] = 'attachment; filename="processed_data.csv"'
描述:获取或设置响应的头部信息。可以通过直接操作字典来访问。
一些问题,为啥这么干
关于为啥excel_file = pd.ExcelFile(BytesIO(content)),即把一个byteio对象传输给excelfile函数而不是直接把content传入
pd.ExcelFile
的构造函数通常接受文件路径、文件对象或类文件对象作为参数。BytesIO
是一个类文件对象,它实现了文件对象的基本接口(如 read()
方法),因此可以被 pd.ExcelFile
接受
关于从前端发送过来数据后,不同类型怎么处理
Django 在处理请求时,会根据请求头中的 Content-Type
来判断数据的格式,并将数据放入不同的属性中。下面是 Django 如何处理不同格式数据的简要说明:
-
Content-Type 为 application/json 即json数据:
- 如果前端发送的数据格式为 JSON,并且 Content-Type 设置为
application/json
,那么 Django 会将整个请求体的数据作为二进制流保存在request.body
中。 - 由于 Content-Type 不是
application/x-www-form-urlencoded
或multipart/form-data
,Django 不会对请求体中的数据进行解析,因此request.POST
和request.FILES
不会有数据。
- 如果前端发送的数据格式为 JSON,并且 Content-Type 设置为
-
Content-Type 为 application/x-www-form-urlencoded 即表单数据:
- 如果前端发送的数据格式为普通表单数据,并且 Content-Type 设置为
application/x-www-form-urlencoded
,Django 会将请求体中的数据解析并保存在request.POST
中。 request.FILES
通常为空,除非请求中同时包含文件上传。
- 如果前端发送的数据格式为普通表单数据,并且 Content-Type 设置为
-
Content-Type 为 multipart/form-data 即文件数据:
- 如果前端发送的数据格式为文件上传数据,并且 Content-Type 设置为
multipart/form-data
,Django 会将请求体中的文件数据解析并保存在request.FILES
中,同时将其他表单字段数据保存在request.POST
中。
- 如果前端发送的数据格式为文件上传数据,并且 Content-Type 设置为
关于django中路由url的配置
1.xxx/xxx/< >形式
path('spider/upload_config_files/<str:id>', upload_config_files, name='upload_config_files'),
假定如此配置到,那么前端请求的方式应为 xxx:/xx/xxx/xx1 其中最后的xx1便会被解析为id
在views.py视图层中,这么设置, 那么id便接收到了xx1为值
@csrf_exempt
def upload_config_files(request,id):
2. xxx/xxx 形式
path('spider/upload_config_files/', upload_config_files, name='upload_config_files'),
假定如此配置到,那么前端请求的方式为 xxx:/xx/xxx/?param=xx1时,那么在视图层函数views.py中
@csrf_exempt
def upload_config_files(request):
print(request.GET.get('param'))
那么便会打印xx1即param的值为xx1