前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="stylesheet" href="./bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="./bootstrap/bootstrap-theme.min.css" />
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background:#f5f5f5;
}
.row_input{
width: 100%;
max-width: 400px;
height: 39px;
margin: 15px auto;
background: #fefefe;
border: 1px solid #cecece;
font-size: 12px;
font-family: sans-serif;
color: #888;
border-radius: 4px;
cursor: pointer;
overflow: hidden;
padding:0!important;
position:relative;
}
.input-caption{
display: block;
height: 100%;
line-height:36px;
padding-left: 10px;
text-overflow: ellipsis;
overflow: hidden;
font-size:14px;
font-weight: normal;
padding-right:100px
}
.close_btn{
width:100%;
margin:0 auto;
max-width:400px;
display: block;
height: 100%;
padding:0;
border: 1px solid #ccc;
line-height: 36px;
border-radius: 4px;
color: #666666;
margin-top: 15px;
text-align: center;
background-color: #fefefe;
background-image: -webkit-gradient(linear,0 0,0 100%,from(#fefefe),to(#f1f1f1));
background-image: -webkit-linear-gradient(top,#fefefe,#f1f1f1);
background-image: -o-linear-gradient(top,#fefefe,#f1f1f1);
background-image: linear-gradient(to bottom,#fefefe,#f1f1f1);
background-image: -moz-linear-gradient(top,#fefefe,#f1f1f1);
-webkit-transition: all .1s ease-out;
-moz-transition: all .1s ease-out;
-o-transition: all .1s ease-out;
transition: all .1s ease-out;
cursor:pointer
}
.input-button{
position:absolute;
right:0;
top:0;
display: block;
height: 100%;
padding:0;
border-left: 1px solid #ccc;
color: #666666;
text-align: center;
background-color: #fefefe;
background-image: -webkit-gradient(linear,0 0,0 100%,from(#fefefe),to(#f1f1f1));
background-image: -webkit-linear-gradient(top,#fefefe,#f1f1f1);
background-image: -o-linear-gradient(top,#fefefe,#f1f1f1);
background-image: linear-gradient(to bottom,#fefefe,#f1f1f1);
background-image: -moz-linear-gradient(top,#fefefe,#f1f1f1);
-webkit-transition: all .1s ease-out;
-moz-transition: all .1s ease-out;
-o-transition: all .1s ease-out;
transition: all .1s ease-out;
cursor:pointer
}
.wrap {
width: 100px;
height: 36px;
background-color: red;
text-align: center;
border-radius:0;
border:0;
padding:0;
}
.wrap p {
width: 100%;
height: 100%;
line-height: 36px;
text-align: center;
}
#file {
position: absolute;
left: 0;
top: 0;
width: 100px;
height: 36px;
display: block;
opacity: 0;
}
.progress {
position: relative;
}
.progress-bar {
transition: width .3s ease
}
.progress .value {
position: absolute;
color: #FF9800;
left: 50%;
}
.container {
width: ;
}
.container label{
display:block;
cursor:pointer;
}
.row {
padding: 10px;
margin:0;
}
.hidden {
display: none;
}
.loading_pro{
margin:0 auto;
max-width:400px;
border: 1px solid #C5C5C5;
box-shadow: 0 0 3px #ccc;
-webkit-box-shadow: 0 0 3px #ccc;
background:white;
margin-top:15px;
padding-bottom:10px;
}
.loading_pro h5{
text-align:center;
line-height:24px;
font-size:16px;
margin-bottom:0;
}
.dotting {
display: inline-block; width: 10px; min-height: 2px;
padding-right: 2px;
border-left: 2px solid currentColor; border-right: 2px solid currentColor;
background-color: currentColor; background-clip: content-box;
box-sizing: border-box;
animation: dot 4s infinite step-start both;
*zoom: expression(this.innerHTML = '...'); /* IE7 */
}
.dotting:before { content: '...'; } /* IE8 */
.dotting::before { content: ''; }
:root .dotting { margin-left: 2px; padding-left: 2px; } /* IE9+ */
@keyframes dot {
25% { border-color: transparent; background-color: transparent; } /* 0个点 */
50% { border-right-color: transparent; background-color: transparent; } /* 1个点 */
75% { border-right-color: transparent; } /* 2个点 */
}
</style>
<title>上传文件</title>
</head>
<body>
<div class="container">
<label for="file">
<div class="row_input">
<div class="input-caption" id = "input-caption">
选择文件上传
</div>
<div class="input-button">
<div class="wrap btn btn-default">
<input type="file" id="file" />
<p class="">上传文件</p>
</div>
</div>
</div>
</label>
<div class="loading_pro">
<h5 id="select" style="display:">请选择视频文件(*.mp4,*.flv),图片(*.png, *.jpg, *.gif)</h5>
<h5 id="uping" style="display:none">上传中<span class="dotting" style="display: "></span></h5>
<h5 id="upend" style="display:none">上传成功</h5>
<div class="row" id="process1" style="display: none">
<div class="col-md-4">
校验文件进度
</div>
<div class="col-md-8">
<div class="progress">
<div id="checkProcessStyle" class="progress-bar" style="width:0%"></div>
<p id="checkProcessValue" class="value">0%</p>
</div>
</div>
</div>
<div class="row" id="process2" style="display: none">
<div class="col-md-4">
上传文件进度
</div>
<div class="col-md-8">
<div class="progress">
<div id="uploadProcessStyle" class="progress-bar" style="width:0%"></div>
<p id="uploadProcessValue" class="value">0%</p>
</div>
</div>
</div>
</div>
<div class="close_btn">
返回
</div>
</div>
<script src="./lib/jquery-1.10.2.min.js"></script>
<script src="./bootstrap/bootstrap.min.js"></script>
<script src="./lib/spark-md5.min.js"></script>
<script>
let baseUrl = 'http://[这里写服务器ip]'
let chunkSize = 5 * 1024 * 1024
let fileSize = 0
let file = null
let hasUploaded = 0
let chunks = 0
// spark = new SparkMD5.ArrayBuffer()
$("#file").on('change', function () {
file = this.files[0]
fileSize = file.size;
responseChange(file)
})
// 0.响应点击
async function responseChange(file) {
document.getElementById("select").style.display="none";
document.getElementById("uping").style.display="";
//document.getElementById("sp").style.display="none";
// 第一步:按照 修改时间+文件名称+最后修改时间-->MD5
// 显示文件校验进度
$("#process1").slideDown(200)
//var a = document.getElementById("input-caption").innerText;
// var a = document.getElementsByClassName("input-caption");
// $(".input-caption").html() = "bbbbbbb";
y = document.getElementById("input-caption");
y.innerHTML = file.name;
// 开始校验
let fileMd5Value = await md5File(file) // 生成
//alert("fileMd5Value =" + fileMd5Value);
// 第二步:校验文件的MD5
let result = await checkFileMD5(file.name, fileMd5Value);
var RESULT = eval(result);
// 如果文件已存在, 就秒传
if (RESULT.filename) {
//alert('文件已秒传');
document.getElementById("process1").style.display="none";
document.getElementById("process2").style.display="none";
var y = document.getElementById("upend");
y.innerHTML = "上传已完成";
document.getElementById("select").style.display="none";
document.getElementById("uping").style.display="none";
document.getElementById("upend").style.display="";
return;
}
let exit = false
// alert("result.chunkList = " + result.chunkList);
// 显示文件上传进度
$("#process2").slideDown(200)
// 第三步:检查并上传MD5
// alert("result.chunkList = " + result.chunkList);
// 第三步:检查并上传MD5
await checkAndUploadChunk(fileMd5Value, RESULT.chunkList, file);
await notifyServer(fileMd5Value, file, chunks);
//alert("hasUploaded" + hasUploaded); // Compute hash
// 第四步: 通知服务器所有分片已上传完成
// notifyServer(fileMd5Value)
}
// 1.修改时间+文件名称+最后修改时间-->MD5
function md5File(file) {
return new Promise((resolve, reject) => {
var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
//chunkSize = 2097152, // Read in chunks of 2MB
chunkSize = file.size / 100,
//chunks = Math.ceil(file.size / chunkSize),
chunks = 100,
currentChunk = 0,
spark = new SparkMD5.ArrayBuffer(),
fileReader = new FileReader();
fileReader.onload = function (e) {
//console.log('read chunk nr', currentChunk + 1, 'of', chunks);
spark.append(e.target.result); // Append array buffer
currentChunk++;
if (currentChunk < chunks) {
loadNext();
} else {
let cur = +(new Date())
//console.log('finished loading');
// alert(spark.end() + '---' + (cur - pre)); // Compute hash
let result = spark.end()
resolve(result)
}
};
fileReader.onerror = function () {
//console.warn('oops, something went wrong.');
};
function loadNext() {
var start = currentChunk * chunkSize,
end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
$("#checkProcessStyle").css({
width: (currentChunk + 1) + '%'
})
$("#checkProcessValue").html((currentChunk + 1) + '%')
// $("#tip").html(currentChunk)
}
loadNext();
})
}
//2.校验文件的MD5
function checkFileMD5(fileName, fileMd5Value) {
return new Promise((resolve, reject) => {
//let url = baseUrl + '/check/file?fileName=' + fileName + "&fileMd5Value=" + fileMd5Value
/*
检查文件是否存在,如果整个文件以存在或不存在返回文件名,如果是存在文件块返回文件块列表(json格式),
filename 上传文件名
fileMd5Value 上传文件MD5值
method 要调用的方法名
*/
let url = baseUrl + '/upload.cgi?filename=' + fileName + "&fileMd5Value=" + fileMd5Value + "&method=" + "check";
$.getJSON(url, function (data) {
resolve(data)
})
})
}
// 3.上传chunk
async function checkAndUploadChunk(fileMd5Value, chunkList, file) {
chunks = Math.ceil(fileSize / chunkSize) //片段数等于文件总大小除去一个片段的大小
//let exit = false;
var List = eval(chunkList);
var hasUploaded = 0;
var len = 0;
if((chunkList != null) && (chunkList != "") && (chunkList != undefined))
{
hasUploaded = List.length
len = List.length;
}
/*获取json文件列表,判断文件列表中的 文件*/
//console.log("chunks: "+ chunks + " List: "+ List.length + " hasUploaded = " + hasUploaded);
for (var i = 0; i < chunks; i++)
{
//let exit = chunkList.indexOf(i + "") > -1
let exit = false;
if(i < len)
{
if((chunkList != null) && (chunkList != "") && (chunkList != undefined) && (chunkList[i].filename != null) && (chunkList[i].filename != undefined) && (chunkList[i].filename != "") )
{
for(var j =0; j < len; j++)
{
if(chunkList[j].filename.indexOf(i + '') > -1)
{
exit = true;
}
}
}
}
// 如果已经存在, 则不用再上传当前块
if (!exit)
{
let index = await upload(i, fileMd5Value, chunks, file);
hasUploaded++;
let radio = Math.floor((hasUploaded / chunks) * 100);
$("#uploadProcessStyle").css({
width: radio + '%'
})
$("#uploadProcessValue").html(radio + '%');
}
}
}
// 3-2. 上传chunk
function upload(i, fileMd5Value, chunks, file) {
return new Promise((resolve, reject) => {
//构造一个表单,FormData是HTML5新增的
let end = (i + 1) * chunkSize >= file.size ? file.size : (i + 1) * chunkSize
let form = new FormData()
form.append("data", file.slice(i * chunkSize, end)) //file对象的slice方法用于切出文件的一部分
form.append("total", chunks) //总片数
form.append("index", i) //当前是第几片
form.append("fileMd5Value", fileMd5Value)
/*
传输文件块
method 请求方法名
*/
$.ajax({
url: baseUrl + '/upload.cgi?method=' + "upload" + "&filename=" + file.name + "&fileMd5Value=" + fileMd5Value,
type: "POST",
data: form, //刚刚构建的form数据对象
async: true, //异步
processData: false, //很重要,告诉jquery不要对form进行处理
contentType: false, //很重要,指定为false才能形成正确的Content-Type
success: function (data) {
resolve(data.desc)
}
})
})
}
// 第四步: 通知服务器所有分片已上传完成
function notifyServer(fileMd5Value, file , chunks) {
document.getElementById("process1").style.display="none";
document.getElementById("process2").style.display="none";
//document.getElementById("sp").style.display="none";
//var y = document.getElementById("uping");
//y.innerHTML = "上传成功";
document.getElementById("select").style.display="none";
document.getElementById("uping").style.display="none";
document.getElementById("upend").style.display="";
/*
发送合并请求
fileMd5Value 文件的MD5值
fileName 文件名
size 文件大小
method 请求的方法名
*/
let url = baseUrl + '/upload.cgi?fileMd5Value=' + fileMd5Value + "&filename=" + file.name + "&size=" + file.size + "&method=" + "merge" + "&chunks=" + chunks;
$.getJSON(url, function (data) {
//alert('上传成功')
})
}
function getDate() {
let d = new Date()
return d.getMinutes() + ':' + d.getSeconds() + ' ' + d.getMilliseconds()
}
</script>
</body>
</html>