后端使用 Python Flask
前端原理实现:
1.获取文件特征码
2.截取文件信息和对文件分段
3.校验服务器是否存在同样特征码的没上传完的文件
4.如过有同样特征码文件获取上传进度
5.否则进度从0开始
6.循环异步按顺序上传分段文件
7.如果上传完成提示成功
后端原理实现:
1.接收request(文件hash)参数
2.判断文件是否是中断上传的
3.如果存在hash文件夹获取文件夹下文件段数量返回给前端
4.不存在返回0或空字符串
5.如果前端返回上传的文件段,则保存文件段并给文件段标识索引
6.如果上传完成合并文件,删除文件段
html代码
代码以单个文件上传为例,使用hashMe.js获取特征码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="md5.js"></script>
<script src="hashme.js"></script>
</head>
<body>
<input type="file" onchange="hhh(this.files[0])" />
<button onclick="uploadCk()">测试</button>
<script>
var up_f;//需要上传的信息
var fileSplitSize = 1024 * 1024 * 2; //以2MB为一个分片
function hhh(f) {
if (true) { //假设这是判断文件大小
var hash = new hashMe(f, function(msg) {
up_f = new Object();
up_f.hash = msg;
up_f.name = f.name;
up_f.size = f.size;
up_f.shardCount = Math.ceil(f.size / fileSplitSize); //总片数
up_f.shard = [];//文件段
for (var i = 0; i < up_f.shardCount; i++) {
var start = i * fileSplitSize;
var end = Math.min(f.size, start + fileSplitSize);
up_f.shard[up_f.shard.length] = f.slice(start, end);//保存分段
}
});
}
}
function uploadCk() { //上传前检查
$.ajax({
url: "/upload_ck",
type: "get",
data: {
hash: up_f.hash
},
success: function(data) {
if (data != "") {
upload(Number(data));//调用上传(索引为服务器存在的文件段索引)
} else {
upload(0);//调用上传
}
}
});
}
function upload(loadIndex) { //上传
var form = new FormData();
form.append("hash", up_f.hash);
form.append("name", up_f.name);
form.append("size", up_f.size);
form.append("shardCount", up_f.shardCount);
form.append("blob", up_f.shard[loadIndex]);
form.append("sdIndex", loadIndex);
console.log("sdIndex:" + loadIndex + ",shardCount:" + up_f.shardCount)
$.ajax({
url: "/upload",
type: "POST",
data: form,
async: true,
processData: false, //很重要,告诉jquery不要对form进行处理
contentType: false, //很重要,指定为false才能形成正确的Content-Type
success: function(data) {
data = Number(data) + 1;
if (data <= up_f.shardCount) {
console.log("data:" + data);
upload(data);
} else {
console.log("上传完毕");
}
}
});
}
</script>
</body>
</html>
Python代码
为示例写的Python代码有些不规范大家尽量不要模仿我这种写法(mime下载)
from flask import Flask, url_for,request
import codecs,re,os
import urllib.parse,mime
import shutil
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter):
def __init__(self, map, *args):
self.map = map
self.regex = args[0]
app = Flask(__name__)
mim=mime.types
app.config['UPLOAD_FOLDER'] = 'uploads/'#保存文件位置
app.url_map.converters['regex'] = RegexConverter
@app.route('/<regex(".*"):url>')
def index(url):
ps=urllib.parse.unquote(url)
if ps=="upload":
return upload()
elif ps.split('?')[0]=="upload_ck":
if os.path.exists("./"+app.config['UPLOAD_FOLDER']+str(request.args.get('hash') ) ):
return str(len( os.listdir("./"+app.config['UPLOAD_FOLDER']+str(request.args.get('hash') )) )-1 )#返回文件段索引
else:
return ""
bt=codecs.open(ps,'rb',"utf-8").read()
return bt, 200, {'Content-Type': mim[url.split(".")[-1]]}
@app.route('/upload', methods=['POST'])
def upload():
hashtxt=request.form['hash']
sPs="./"+app.config['UPLOAD_FOLDER']+hashtxt+"/"
if not os.path.exists(sPs):#文件夹不存在
os.makedirs(sPs)#创建hash文件夹
uploaded_files = request.files.getlist("blob")#获取文件流集
filePs=hashtxt+"/"+request.form['name']+".part"+request.form['sdIndex'] #文件段保存路径
for file in uploaded_files:
file.save(os.path.join(app.config['UPLOAD_FOLDER'],filePs ))#保存文件
if (int(request.form['shardCount']))==(int(request.form['sdIndex'])):#判断上传完最后一个文件
mergeFile(app.config['UPLOAD_FOLDER'],request.form['name'],hashtxt);#合并文件
shutil.rmtree("./"+app.config['UPLOAD_FOLDER']+hashtxt)#删除
return request.form['sdIndex']#返回段索引
def mergeFile(ps,nm,hs):#合并文件
temp = open(ps+"/"+nm,'wb')#创建新文件
count=len(os.listdir(ps+"/"+hs))
for i in range(0,count):
fp = open(ps+"/"+hs+"/"+nm+".part"+str(i), 'rb')#以二进制读取分割文件
temp.write(fp.read())#写入读取数据
fp.close()
temp.close()
with app.test_request_context(): #输出url
pass
if __name__ == '__main__':
app.debug = True
app.run()