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 mainimport ( "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)}