哈哈哈哈哈哈,真的好丑。。。。
1.实现思路
首先是一个文件上传的区域,本身就是大的label元素,绑定了一个隐藏的input type=file,然后就往后传文件,监听文件变化后就将这个label隐藏,切换出显示进度条的页面,等进度条完成后再切换预览页面。
代码很粗糙,仅限于写个demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.upload-area,
.uploading,
.after-upload {
width: 250px;
height: 250px;
border-radius: 5px;
background-color: rgb(240, 234, 234);
box-shadow: 5px 5px 5px rgb(190, 190, 190);
display: flex;
justify-content: center;
align-items: center;
}
.uploading {
flex-direction: column;
}
.before-upload {
width: 100%;
height: 100%;
}
.label-upload {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.upload-area.status-0>.uploading,
.upload-area.status-0>.after-upload {
display: none;
}
.upload-area.status-1>.before-upload,
.upload-area.status-1>.after-upload {
display: none;
}
.upload-area.status-2>.before-upload,
.upload-area.status-2>.uploading {
display: none;
}
.progress {
margin: 0 10%;
width: 80%;
height: 20px;
border-radius: 10px;
background-color: rgba(250, 250, 250, 0.5);
box-shadow: 1px 1px 1px rgba(148, 148, 148, 0.3);
}
.progress>.progress-bar {
width: var(--bar-width);
height: 20px;
border-radius: 10px;
background-color: rgb(70, 111, 201);
}
.progress>.progress-str {
color: rgb(119, 119, 119);
}
.img-proview {
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-size: contain;
}
#uploadFile{
position: absolute;
opacity: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
</style>
</head>
<body>
<div class="upload-area status-0">
<div class="before-upload">
<input type="file" id="uploadFile" onchange="uploadFile()" />
<div class="label-upload">
<svg t="1685411854880" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="2357" width="80" height="80">
<path
d="M930.738818 418.535831H605.170853V93.238515A93.080637 93.080637 0 0 0 512.135324 0a93.058083 93.058083 0 0 0-93.012974 93.261069v325.500303H93.396506a93.306177 93.306177 0 0 0-93.328731 93.261069 93.441502 93.441502 0 0 0 27.222759 66.173634 92.967866 92.967866 0 0 0 65.857878 27.561071h325.973938V930.806367c0 25.756745 10.216993 49.077651 27.087435 65.902986a92.697218 92.697218 0 0 0 65.835323 27.312976 93.148299 93.148299 0 0 0 93.125745-93.215962V605.734592h325.567965c51.490936 0 93.238515-42.108444 93.215961-93.59938-0.022554-51.468382-41.770133-93.59938-93.215961-93.599381z"
p-id="2358" fill="#cdcdcd"></path>
</svg>
</div>
</div>
<div class="uploading">
<div class="progress-str"><span class="number">0</span>%</div>
<div class="progress">
<div class="progress-bar" style="--bar-width: 0%"></div>
</div>
</div>
<div class="after-upload">
<div class="img-proview">
</div>
</div>
</div>
<script>
const uploadArea = document.querySelector(".upload-area");
const progressBar = document.querySelector(".progress>.progress-bar");
const progressNumber = document.querySelector(".uploading .number");
function changeProgress(progress) {
progressBar.setAttribute("style", `--bar-width:${progress}%`);
progressNumber.innerText = `${progress}`;
}
async function uploadFile() {
uploadArea.setAttribute("class", "upload-area status-1")
const fileInput = document.querySelector("#uploadFile");
if (fileInput.files.length === 0) {
alert("请选择上传文件");
return null;
}
upload(fileInput.files[0], oncomplete)
}
function upload(file, oncomplete) {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
const resp = JSON.parse(xhr.responseText);
oncomplete(resp);
}
const formData = new FormData();
formData.append('file', file);
xhr.upload.onprogress = (e) => {
console.log(e);
// e下面有loaded属性为已经上传了多少数据,total为上传文件的总进度
changeProgress(Math.floor(e.loaded / e.total * 100));
};
xhr.open("POST", "/local-api/file/singleFileUpload", true);
xhr.send(formData);
}
function oncomplete(resp) {
console.log(resp);
uploadArea.setAttribute("class", "upload-area status-2")
document.querySelector(".img-proview").setAttribute("style", `background-image: url("/local-api/${resp.data.src.replaceAll("\\", "/")}");`);
}
</script>
</body>
</html>
2拖拽上传
要想实现拖拽上传很简单只需要把上面的input取消隐藏,然后放大到撑满整个容器,设置opacity为0即可。但是,如果不想这么写,(是的,我不想),那么可以借助h5的ondrayenter、ondrayover、ondrop等api实现自己的一个可拖拽容器。
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.upload-area,
.uploading,
.after-upload {
width: 250px;
height: 250px;
border-radius: 5px;
background-color: rgb(240, 234, 234);
box-shadow: 5px 5px 5px rgb(190, 190, 190);
display: flex;
justify-content: center;
align-items: center;
}
.uploading {
flex-direction: column;
}
.before-upload {
width: 100%;
height: 100%;
}
.label-upload {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.upload-area.status-0>.uploading,
.upload-area.status-0>.after-upload {
display: none;
}
.upload-area.status-1>.before-upload,
.upload-area.status-1>.after-upload {
display: none;
}
.upload-area.status-2>.before-upload,
.upload-area.status-2>.uploading {
display: none;
}
.progress {
margin: 0 10%;
width: 80%;
height: 20px;
border-radius: 10px;
background-color: rgba(250, 250, 250, 0.5);
box-shadow: 1px 1px 1px rgba(148, 148, 148, 0.3);
}
.progress>.progress-bar {
width: var(--bar-width);
height: 20px;
border-radius: 10px;
background-color: rgb(70, 111, 201);
}
.progress>.progress-str {
color: rgb(119, 119, 119);
}
.img-proview {
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-size: contain;
position: relative;
}
.img-proview>.close-button{
width: 38px;
height: 38px;
border-radius: 50%;
background-image: radial-gradient(rgb(173, 170, 170), transparent );
filter: drop-shadow(1px 1px 1px rgba(148, 148, 148, 0.3));
position: absolute;
top: 0;
right: 0;
cursor: pointer;
}
</style>
</head>
<body>
<div class="upload-area status-0">
<div class="before-upload">
<input type="file" id="uploadFile" style="display: none;" onchange="uploadFile()" />
<label for="uploadFile" class="label-upload">
<svg t="1685411854880" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="2357" width="80" height="80">
<path
d="M930.738818 418.535831H605.170853V93.238515A93.080637 93.080637 0 0 0 512.135324 0a93.058083 93.058083 0 0 0-93.012974 93.261069v325.500303H93.396506a93.306177 93.306177 0 0 0-93.328731 93.261069 93.441502 93.441502 0 0 0 27.222759 66.173634 92.967866 92.967866 0 0 0 65.857878 27.561071h325.973938V930.806367c0 25.756745 10.216993 49.077651 27.087435 65.902986a92.697218 92.697218 0 0 0 65.835323 27.312976 93.148299 93.148299 0 0 0 93.125745-93.215962V605.734592h325.567965c51.490936 0 93.238515-42.108444 93.215961-93.59938-0.022554-51.468382-41.770133-93.59938-93.215961-93.599381z"
p-id="2358" fill="#cdcdcd"></path>
</svg>
</label>
</div>
<div class="uploading">
<div class="progress-str"><span class="number">0</span>%</div>
<div class="progress">
<div class="progress-bar" style="--bar-width: 0%"></div>
</div>
</div>
<div class="after-upload">
<div class="img-proview">
<div class="close-button" onclick="closeCurrentImg()">
<svg t="1685436626958" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1443" width="38" height="38"><path d="M512 85.333333C276.352 85.333333 85.333333 276.352 85.333333 512s191.018667 426.666667 426.666667 426.666667 426.666667-191.018667 426.666667-426.666667S747.648 85.333333 512 85.333333zM671.061333 662.848c-19.242667 19.221333-50.410667 19.221333-69.632 0l-81.216-81.216-81.216 81.216c-19.242667 19.221333-50.410667 19.221333-69.632 0-19.221333-19.242667-19.221333-50.410667 0-69.632L450.581333 512l-81.237333-81.216c-19.221333-19.242667-19.221333-50.410667 0-69.632 19.242667-19.221333 50.410667-19.221333 69.632 0l81.216 81.216 81.216-81.216c19.242667-19.221333 50.410667-19.242667 69.632 0 19.221333 19.242667 19.221333 50.410667 0 69.632L589.824 512l81.237333 81.216C690.282667 612.458667 690.304 643.626667 671.061333 662.848z" fill="#dbdbdb" p-id="1444"></path></svg>
</div>
</div>
</div>
</div>
<script>
const uploadArea = document.querySelector(".upload-area");
const beforeUpload = document.querySelector(".label-upload");
const progressBar = document.querySelector(".progress>.progress-bar");
const progressNumber = document.querySelector(".uploading .number");
// 注册div的拖拽api
beforeUpload.ondragenter = e => {
e.preventDefault();
};
beforeUpload.ondragover = e => {
e.preventDefault();
};
beforeUpload.ondrop = e => {
e.preventDefault();
document.querySelector("#uploadFile").files = e.dataTransfer.files
uploadFile();
};
function changeProgress(progress) {
console.log("progress", progress)
progressBar.setAttribute("style", `--bar-width:${progress}%`);
progressNumber.innerText = `${progress}`;
}
async function uploadFile() {
uploadArea.classList.replace("status-0", "status-1");
const fileInput = document.querySelector("#uploadFile");
if (fileInput.files.length === 0) {
alert("请选择上传文件");
return null;
}
upload(fileInput.files[0], oncomplete)
}
function upload(file, oncomplete) {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
const resp = JSON.parse(xhr.responseText);
oncomplete(resp);
}
const formData = new FormData();
formData.append('file', file);
xhr.upload.onprogress = (e) => {
console.log(e);
// e下面有loaded属性为已经上传了多少数据,total为上传文件的总进度
changeProgress(Math.floor(e.loaded / e.total * 100));
};
xhr.open("POST", "/local-api/file/singleFileUpload", true);
xhr.send(formData);
}
function oncomplete(resp) {
console.log(resp);
uploadArea.classList.replace("status-1", "status-2");
document.querySelector(".img-proview").setAttribute("style", `background-image: url("/local-api/${resp.data.src.replaceAll("\\", "/")}");`);
}
function closeCurrentImg(){
uploadArea.classList.replace("status-2", "status-0");
document.querySelector("#uploadFile").files = [];
}
</script>
</body>
</html>
3.后端代码
后端使用egg写的,话说tegg出来之后我是第一次用,而且看了文档之后还是懵逼,摸索了好几个小时才差不多搞懂了写法(我觉得还不是正规军)。唉
对应的后端代码