实现步骤:
定义
UI
结构
验证是否选择了文件
向
FormData
中追加文件
使用
xhr
发起上传文件的请求
监听
onreadystatechange
事件
<link rel="stylesheet" href="./lib/bootstrap.css" />
<script src="./lib/jquery.js"></script>
<!-- 1. 文件选择框 -->
<input type="file" id="file1" /> <br />
<!-- 2. 上传按钮 -->
<button id="btnUpload">上传文件</button><br />
<!-- bootstrap 中的进度条 -->
<div class="progress" style="width: 500px; margin: 15px 10px">
<div
class="progress-bar progress-bar-striped active"
style="width: 0%"
id="percent"
>
0%
</div>
</div>
<!-- 3. 显示上传到服务器上的图片 -->
<img src="" alt="" id="img" width="400" />
// 1. 获取上传文件的按钮
const btn = document.querySelector("#btnUpload");
// 2. 为按钮添加 click 事件监听
btn.addEventListener("click", function () {
// 3. 获取到选择的文件列表
const files = document.querySelector("#file1").files;
if (files.length <= 0) {
return alert("请选择文件");
}
//4.向FormData中追加文件
const fd = new FormData();
//将用户选择的文件添加到 FormData 中
fd.append("avatar", files[0]);
//5.使用 xhr 发起上传文件的请求
const xhr = new XMLHttpRequest();
//5.1 监听onreadystatechange事件
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const data = JSON.parse(xhr.responseText);
if (data.status === 200) {
//上传成功
// console.log(data);
document.querySelector("#img").src =
"http://www.liulongbin.top:3006" + data.url;
} else {
//上传失败
console.log("文件上传失败!" + data.message);
}
}
};
//6.通过监听 xhr.upload.onprogress 事件,来获取到文件的上传进度
xhr.upload.onprogress = function (e) {
// e.lengthComputable 是一个布尔值,表示当前上传的资源是否具有可计算的长度
if (e.lengthComputable) {
// e.loaded 已传输的字节
// e.total 需传输的总字节
const percentComplete = Math.ceil((e.loaded / e.total) * 100);
// console.log(percentComplete);
//7.监听上传进度--动态设置进度条
$('#percent').attr('style','width: ' + percentComplete + '%').html(percentComplete + '%');
}
};
//8.监听上传完成的事件
xhr.upload.onload = function(){
$('#percent').removeClass().addClass('progress-bar progress-bar-success');
};
xhr.open("POST", "http://www.liulongbin.top:3006/api/upload/avatar");
xhr.send(fd);
});
方式2:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="../../../02bootstrap/css/bootstrap.css" />
<script src="../../../02bootstrap/js/jquery-3.5.1.js"></script>
<script src="../../../02bootstrap/js/bootstrap.js"></script>
<style>
td,
th {
text-align: center;
}
</style>
</head>
<body>
<form action="" id="form">
<p>
请选择文件:
<input type="file" name="file" id="file" multiple />
</p>
<p>
<input type="button" value="上传" id="upload" />
</p>
</form>
<div id="previews">
<table class="table table-bordered table-active" id="tb">
<thead>
<tr>
<th width="10%">文件名称</th>
<th width="10%">文件大小(KB)</th>
<th width="10%">文件类型</th>
<th width="35%">预览图</th>
<th width="35%">上传进度</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
</div>
<script>
// 0. 获取相关元素
const file = document.getElementById("file");
const tbody = document.getElementById("tbody");
const upload = document.getElementById("upload");
// 1. 文件域如何取值(取文件)
// console.log(document.getElementById("file").files);
/**
* 2. 取出的 fileList 怎么用
* 通过 fileReader 去读取每一个 file 对应的内容,读取出文件内容,将文件内容渲染到图片标签上
*/
// 给文件域添加事件--onchange事件
file.onchange = function () {
let files = this.files;
// 在渲染文件之前先清空之前内容区域的内容
tbody.innerHTML = "";
for (let i = 0; i < files.length; i++) {
// 获取文件输入流对象--用户读取文件内容
let reader = new FileReader();
// 异步,调用 readAsDataURL() 传入 file 对象读取该文件的内容
reader.readAsDataURL(files[i]);
reader.onload = function () {
// 拿到读取的文件内容
let result = reader.result;
let tr = document.createElement("tr");
tr.innerHTML = `
<td>${files[i].name}</td>
<td>${Math.ceil(files[i].size / 1024)}KB</td>
<td>${files[i].type}</td>
<td>
<img src="${result}" width="200px" height="120px" />
</td>
<td>
<div class="progress">
<div class="progress-bar progress-bar-animated progress-bar-striped" style="width: 0%"></div>
</div>
</td>
`;
// console.log(tr.nodeType);
tbody.appendChild(tr);
};
}
};
// 3. 文件上传
upload.onclick = function () {
uploadFile();
};
// 定义全局变量 index 记录上传序号
let index = 0;
// 4. 封装文件上传函数
function uploadFile() {
// 获取到选择的文件列表
const files = document.querySelector("#file").files;
if (files.length <= 0) {
return alert("请选择文件");
}
// 准备数据
const formData = new FormData();
let file = files[index];
formData.append("file", file);
// 调用接口完成上传
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const data = JSON.parse(xhr.responseText);
if (data.status === 200) {
// alert("上传成功!");
console.log("上传成功!");
console.log(data.url);
// 每上传一个文件 index 就加1
index++;
// 当 index 小于上传的文件的个数时可以上传,否则结束上传并将 index = 0,便于下次上传
if (index < files.length) {
uploadFile();
} else {
index = 0;
}
} else {
console.log("文件上传失败!");
console.log(data.message);
}
}
};
// 进度条--通过监听 xhr.upload.onprogress 事件,来获取到文件的上传进度
xhr.upload.onprogress = function (e) {
// e.lengthComputable 是一个布尔值,表示当前上传的资源是否具有可计算的长度
if (e.lengthComputable) {
// e.loaded 已传输的字节
// e.total 需传输的总字节
const percentComplete = Math.ceil((e.loaded / e.total) * 100) + "%";
// 监听上传进度--动态设置进度条
document.querySelectorAll(".progress-bar")[index].style.width = percentComplete;
}
};
xhr.open("POST", "http://www.liulongbin.top:3006/api/upload/avatar");
xhr.send(formData);
}
</script>
</body>
</html>