Html 代码
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.container{
display: flex;
flex-wrap: wrap;
background: #2c3e50;
min-height: 50px;
}
.file{
width: 24%;
height: 100px;
background: #eee;
padding: 10px;
margin: 0.5%;
box-sizing: border-box;
}
.progress{
height: 20px;
background-color:#f7f7f7;
box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);
border-radius:4px;
background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);
}
.finish{
background-color: #149bdf;
background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);
background-size:40px 40px;
display: inline-block;
height: 20px;
}
form{
margin-top: 50px;
}
</style>
</head>
<body>
<div class="container">
<!-- <div class="file">-->
<!-- <div class="progress">-->
<!-- <span id="finish" style="width: 0%;" progress="0"></span>-->
<!-- </div>-->
<!-- <input type="button" value="停止" id="stop">-->
<!-- </div>-->
</div>
<input type="file" name="file" id="file" multiple accept="*" >
<script>
var fileBtn = document.getElementById("file");
//触发上传开始
fileBtn.onchange = function(){
for (let i=0;i<this.files.length;i++){
file=this.files[i];
r = new UploadFile(file)
r = null;
}
}
class UploadFile{
file
trigger=true;
tasks= new Array();
constructor(file) {
this.file=file;
this.tasks= this.regTask(file);
this.createDom();
this.upload(this.file,this.tasks);
}
//把文件分割注册成为待发送的任务
regTask(file) {
const FILE_NAME = file.name; //文件名
const FILE_SIZE = file.size; //总大小
const CHUNK_SIZE = 1024 * 1024; // 1MB 分片大小
const CHUNK_TOTAL_NUM = Math.ceil(FILE_SIZE/CHUNK_SIZE); //文件分片数量
let start = 0; //切割起始位置
let end = CHUNK_SIZE; //切割起始位置
let tasks=new Array();//注册一个任务列表数组
for (let i=0;i<CHUNK_TOTAL_NUM;i++){
let task={
fileName:FILE_NAME,
fileSize:FILE_SIZE,
chunkNum:CHUNK_TOTAL_NUM,
chunkName:FILE_NAME+"_"+i,
start:start,
end:end,
}
tasks.push(task);
//更新下一个切割分片的位置
start = end;
end = start + CHUNK_SIZE;
}
return tasks;
}
//切割文件分片
sliceFile(file,start,end){
let chunk = file.slice(start,end);
return chunk;
};
//创建上传的dom
createDom(){
//从模版创建上传dom
let template=`
<div class="file">
<div class="progress">
<span class="finish" style="width: 0%;" progress="0"></span>
</div>
<input type="button" value="停止" class="stop">
</div>
`;
let doc =new DOMParser().parseFromString(template,'text/html');
let node= doc.querySelector('.file');
let selfClass=this
node.querySelector('.stop').onclick=function () {
if (selfClass.trigger==false){
selfClass.trigger=true
this.value="停止"
if (selfClass.file){selfClass.upload();}
}else{
selfClass.trigger=false
this.value="继续"
}
console.log(selfClass.trigger)
};
//
let container=document.querySelector(".container");
container.appendChild(node);
this.node=node;
}
//执行上传
upload() {
console.log(this.tasks.length)
console.log(this.file)
console.log(this.node)
if (this.tasks.length==0){ return} //任务全部完成后停止
if (this.trigger==false){ return} //暂停
//取任务并把文件切片
let task=this.tasks.shift()
let chunk=this.sliceFile(this.file,task.start,task.end)
//准备ajax发送数据
let formData = new FormData();
formData.append('chunk',chunk);
formData.append('chunkSize',chunk.size);
formData.append('chunkName',task.chunkName);
formData.append('chunkNum',task.chunkNum);
formData.append('fileName',task.fileName);
formData.append('fileSize',task.fileSize);
let selfClass=this
let xhr = new XMLHttpRequest();
xhr.open('POST', '/upload', true);
xhr.onload = function(e) {
let res=JSON.parse(this.response)
if(this.status == 200 && res.Code==200){
//返回成功后更新进度条样式
let progress = Math.min(100,((task.chunkNum-selfClass.tasks.length)/task.chunkNum)* 100 ) +'%';
selfClass.node.querySelector('.finish').style.width = progress;
//服务器返回成功
selfClass.upload()
}else{
//服务器返回错误重试
selfClass.tasks.unshift(task)
selfClass.upload()
}
};
xhr.onerror = function(e){
//网络错误时重试
setTimeout(function () {
selfClass.upload();
},1000)
};
xhr.send(formData);
}
}
</script>
</body>
</html>
Golang伪代码
package main
import (
"encoding/json"
"fmt"
"html/template"
"net/http"
)
func main() {
http.HandleFunc("/upload", upload)
http.HandleFunc("/", index)
//启动
err := http.ListenAndServe("127.0.0.1:8080", nil)
fmt.Println(err)
}
func index(res http.ResponseWriter, req *http.Request) {
t := template.New("")
t, _ = t.ParseFiles("index.html")
t.ExecuteTemplate(res, "index.html", map[string]string{})
}
func upload(res http.ResponseWriter, req *http.Request) {
res.Header().Set("content-type", "application/json")
fmt.Println(req.FormFile("chunk"))
fmt.Println(req.FormValue("chunkSize"))
/* 校验文件是否存在,大小,文件名
校验块是否存在,大小,块名
校验块数量是否全匹配
是:合并块,校验文件大小,删除块
读取块写入本地
*/
//返回成功状态
m := struct {
Code int //状态码
Progress int //已获取块数量
}{200, 50}
result, _ := json.Marshal(m)
res.WriteHeader(200)
res.Write(result)
}