需求: 超过5G的视频文件,从windows磁盘,通过web服务上传到服务器的指定目录下
方式: 分块上传(前端引用webuploader框架封装好的上传方式)
原理: 前端分块每次上传20M,传入一个task_id, 后台接到文件后每次存一个文件task_id + num。待前端上传完成所有模块后,后台按照文件num顺序合成数据。
前端可执行代码:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="{{static_url('bootstrap-table/jquery-1.11.1.min.js')}}"></script>
<script src="{{static_url('bootstrap-table/bootstrap.min.js')}}"></script>
<script src="{{static_url('webuploader/webuploader.min.js')}}"></script>
<script src="{{static_url('js/checkFormat.js')}}"></script>
<link rel="stylesheet" type="text/css" href="{{static_url('webuploader/webuploader.css')}}">
<link rel="stylesheet" type="text/css" href="{{static_url('AdminLTE-2.4.5/bower_components/bootstrap/dist/css/bootstrap.min.css')}}">
</head>
<body>
<div>
<div id="picker">请选择</div> <!-- 上传按钮,必须指定id选择器的值 -->
<div class="progress"> <!-- 进度条 -->
<div class="progress-bar progress-bar-striped active" role="progressbar" style="width:0%;"></div>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
var task_id = WebUploader.Base.guid(); //产生task_id
// console.log("-------task_id------" + task_id)
var uploader = WebUploader.create({ //创建上传控件
swf: "{{static_url('webuploader/Uploader.swf')}}", //swf位置,这个可能与flash有关
server: '/cms?command=multi_upload_part', //接收每一个分片的服务器地址
pick: '#picker', //填上传按钮的id选择器值
auto: true, //选择文件后,是否自动上传
chunked: true, //是否分片
chunkSize: 20 * 1024 * 1024, //每个分片的大小,这里为20M
chunkRetry: 3, //某分片若上传失败,重试次数
threads: 1, //线程数量,考虑到服务器,这里就选了1
duplicate: true, //分片是否自动去重
formData: { //每次上传分片,一起携带的数据
task_id: task_id,
},
});
uploader.on('startUpload', function() { //开始上传时,调用该方法
$('.progress-bar').css('width', '0%');
$('.progress-bar').text('0%');
});
uploader.on('uploadProgress', function(file, percentage) { //一个分片上传成功后,调用该方法
$('.progress-bar').css('width', percentage * 100 - 1 + '%');
$('.progress-bar').text(Math.floor(percentage * 100 - 1) + '%');
});
uploader.on('uploadSuccess', function(file) { //整个文件的所有分片都上传成功,调用该方法
//上传的信息(文件唯一标识符,文件名)
// var task_id = "wu_1eddvs99e1m9q1lfsk8e1c011ro40";
var data = {'task_id': task_id, 'filename': file.source['name'] };
$.get('/cms?command=multi_upload_success', data); //ajax携带data向该url发请求
$('.progress-bar').css('width', '100%');
$('.progress-bar').text('上传完成');
});
uploader.on('uploadError', function(file) { //上传过程中发生异常,调用该方法
$('.progress-bar').css('width', '100%');
$('.progress-bar').text('上传失败');
});
uploader.on('uploadComplete', function(file) {//上传结束,无论文件最终是否上传成功,该方法都会被调用
$('.progress-bar').removeClass('active progress-bar-striped');
});
});
</script>
</body>
</html>
后端代码:
'''
# 上传
'''
def multi_upload_part(self):
upload_file = self.request.files['file']
task = self.get_body_argument('task_id') # 获取文件唯一标识符
chunk = self.get_body_argument('chunk', 0) # 获取该分片在所有分片中的序号
filename_temp = '%s%s' % (task, chunk) # 构成该分片唯一标识符
for meta in upload_file:
filename_old = meta['filename']
# 数据流存储
with open('/root/upload_test/%s' % filename_temp, 'wb') as f:
f.write(meta['body'])
# 文件名称存储在临时文件
with open('/root/upload_test/%s' % (str(task) + "_filename"), 'wt') as f:
f.write(filename_old)
return self.write({"status": 1, "res": "接收成功"})
'''
# 上传成功
'''
def multi_upload_success(self): # 按序读出分片内容,并写入新文件
target_filename = self.get_argument('filename', '') # 获取上传文件的文件名
task_id = self.get_argument('task_id') # 获取文件的唯一标识符
temp_path = str(task_id) + "_filename"
temp_filename_path = "/root/upload_test/" + temp_path
if target_filename == "":
with open(temp_filename_path, "rt") as f:
data = f.read()
target_filename = data
chunk = 0 # 分片序号
with open('/root/upload_test/%s' % target_filename, 'wb') as target_file: # 创建新文件
while True:
try:
filename = '/root/upload_test/%s%d' % (task_id, chunk)
source_file = open(filename, 'rb') # 按序打开每个分片
target_file.write(source_file.read()) # 读取分片内容写入新文件
source_file.close()
except Exception as e:
print("********error = ", e)
break
chunk += 1
sys_pyutil.remove_file(filename)
sys_pyutil.remove_file(temp_filename_path) # 删除该临时文件,节约空间
return self.write({"status": 1, "res": "接收成功"})
'''
删除临时文件
'''
def remove_file(filePath):
if os.path.exists(filePath):
os.remove(filePath)
return {"status": 1, "msg": "删除成功"}
else:
return {"status": 0, "msg": "文件不存在"}
到这里就结束了,可以愉快的开始上传了
参考链接:
https://blog.csdn.net/jinixin/article/details/77545140
https://github.com/jinixin/upload-demo
https://github.com/jinixin/upload-demo/blob/master/server.py