断点续传补充–手动暂停上传
前文提到大文件切片上传以及断点续传,但是这里我们不能够手动暂停切片上传,停止不了ajax的发送。这篇文章用于实现这个功能。
网上提到停止的ajax需要使用abort()函数,但是基于我上传的方式,abort不能够完全立即停止,所以我才用setinterval和clearinterval;
前端代码:index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.10.0/js/md5.min.js"></script>
<!-- <link rel="stylesheet" type="text/css" href="./webupload/bootstrap/css/bootstrap.min.css"> -->
</head>
<style>
</style>
<body>
<div id="fileUploadContainer">
<select style="width: 150px;float: left;" class="custom-select custom-select-lg mb-3" id="selected"
onchange="changeChoose()">
<option selected value="file">选择文件</option>
<option value="folder">选择文件夹</option>
</select>
<div style="width: auto;float: left;">
<input type="file" multiple class="file uploadfile" id="file" value="选择文件" />
<input type="file" webkitdirectory class="folder uploadfile" id="folder" value="选择文件夹"
style="display: none;" />
</div>
<progress id="progress" max="100" value="0"></progress>
<div id="uploadStatus" class="webuploader-pick" style="float: left; margin-right: 10px" status="upload"
onclick="changeStatus(this)">上传</div>
</div>
<script>
var timer;
var chunkSize = 10 * 1024 * 1024;
var start = 0;
var index = 0;
var fileTotalSize = 0;
// 设定是上传文件夹还是文件
function changeChoose() {
var obj = document.getElementById("selected"); //定位id
var index = obj.selectedIndex; // 选中索引
var text = obj.options[index].text; // 选中文本
var value = obj.options[index].value; // 选中值
if (value == 'file') {
document.getElementById('file').style.display = 'block';
document.getElementById('folder').style.display = 'none';
}
if (value == 'folder') {
document.getElementById('file').style.display = 'none';
document.getElementById('folder').style.display = 'block';
}
}
//继续上传和暂停上传
function changeStatus(e) {
var status = document.getElementById('uploadStatus').getAttribute('status');
if (status == "upload") {
document.getElementById('uploadStatus').innerHTML = "暂停上传";
document.getElementById('uploadStatus').setAttribute("status", "suspend");
upload();
}
else {
document.getElementById('uploadStatus').innerHTML = "继续上传";
document.getElementById('uploadStatus').setAttribute("status", "upload");
clearInterval(timer);
}
}
function upload() {
let fileObj = document.getElementById('file').files[0];
let fd = new FormData();
var md5Val = md5(fileObj.name);
fileTotalSize = fileObj.size;
fd.append('md5', md5Val);
fd.append('fileTotalSize', fileObj.size);
fd.append('filename', fileObj.name);
// 判断文件是否存在
timer = setInterval(test,1000,fd);
}
function test(fd){
ajax('/file/isExist', 'POST', fd, isExist)
}
function isExist(res) {
var result = JSON.parse(res);
if (result.isFileExist == 'isExist') {
alert('文件存在,请改变名称或者上传其他文件!');
return;
} else {
if (result.chunk == 0) {
start = 0;
index = 0;
} else {
start = result.chunk * chunkSize;
index = result.chunk;
}
uploadChunk(chunkSize, start, index);
}
}
function uploadChunk(chunkSize, start, index) {
let fileObj = document.getElementById('file').files[0];
// 文件名md5
var md5Val = md5(fileObj.name);
// 上传完成
if (start >= fileObj.size) {
let fd = new FormData();
fd.append('md5', md5Val);
fd.append('filename', fileObj.name);
ajax('/file/merge', 'POST', fd, mergeChunk);
clearInterval(timer);
document.getElementById('file').files[0] = "";
return;
}
// 获取文件块的终止字节
let end = (start + chunkSize > fileObj.size) ? fileObj.size : (start + chunkSize);
// 将文件切块上传
let fd = new FormData();
fd.append('file', fileObj.slice(start, end));
fd.append('md5', md5Val);
fd.append('chunk', index);
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('POST', 'http://127.0.0.1:5000/isUpload', true);
xmlhttp.onload = function () {
if (this.readyState == 4 && this.status == 200) {
// 上传一块完成后修改进度条信息,然后上传下一块
let progress = document.getElementById('progress');
progress.max = fileObj.size;
progress.value = end;
}
}
xmlhttp.send(fd);
}
function mergeChunk(res) {
console.log('文件生成成功!');
}
//ajax model
function ajax(url, method, msg, fn, id, innerHTMLId) {
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
fn(xmlhttp.responseText, url, id, innerHTMLId)
}
}
xmlhttp.open(method, url, true);
xmlhttp.send(msg);
}
</script>
</body>
</html>
后端代码:upload.py
from flask import Flask,session,request,jsonify,abort
from flask.templating import render_template
from flask_cors import CORS
import hashlib,os
from werkzeug.utils import secure_filename
from werkzeug.datastructures import FileStorage
import threading
import time
import inspect
import ctypes
app=Flask(__name__)
CORS(app, supports_credentials=True)
threadArr = {}
chunkSize = 10 * 1024 * 1024;
@app.route("/")
def home():
return render_template('index.html')
@app.route("/isUpload",methods = ['GET','POST'])
def isUpload():
if request.method == 'POST':
# 存储切片
md5Val = request.form.get('md5') # 获取文件的唯一标识符
chunk = request.form.get('chunk', 0) # 获取该分片在所有分片中的序号
filename = '%s%s' % (md5Val, chunk) # 构造该分片的唯一标识符
upload_file = request.files['file']
upload_file.save('./chunk/%s' % filename) # 保存分片到本地
return 'isUpload'
@app.route('/file/isExist', methods=['POST'])
def isExist():
target_filename = request.form.get('filename')
md5Val = request.form.get('md5')
fileTotalSize = request.form.get('fileTotalSize')
chunk = 0 # 分片序号
chunkTotalSize = 0
result = {}
isFileExist='notExist'
if os.path.exists('./uploadfile/%s' % target_filename):
isFileExist='isExist'
else:
isFileExist='notExist'
while True:
try:
filename = './chunk/%s%d' % (md5Val, chunk)
if os.path.exists(filename):
chunk += 1
chunkTotalSize += os.path.getsize(filename)
else:
break
except IOError as msg:
break
result['isFileExist'] = isFileExist
result['chunk'] = chunk
result['chunkTotalSize'] = chunkTotalSize
print(result)
return result
@app.route('/file/merge', methods=['POST'])
def upload_success(): # 按序读出分片内容,并写入新文件
md5Val = request.form.get('md5') # 获取文件的唯一标识符
target_filename = request.form.get('filename') # 获取该分片在所有分片中的序号
chunk = 0 # 分片序号
with open('./uploadfile/%s' % target_filename, 'wb') as target_file: # 创建新文件
while True:
try:
filename = './chunk/%s%d' % (md5Val, chunk)
source_file = open(filename, 'rb') # 按序打开每个分片
target_file.write(source_file.read()) # 读取分片内容写入新文件
source_file.close()
except IOError as msg:
break
chunk += 1
os.remove(filename) # 删除该分片,节约空间
return '文件上传成功!'
if __name__=='__main__':
app.run(debug=True)