大文件上传
True
无标题
前端分片上传,分片全部上传完成后在后台合并文件。
前端相关代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
<!-- import CSS -->
<link rel="stylesheet" href="/funenc_driver/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/funenc_driver/static/js_res/webuploader/webuploader.css">
<!-- import JavaScript -->
<script src="/funenc_driver/static/js_res/jquery.js"></script>
<script src="/funenc_driver/static/js_res/bootstrap.min.js"></script>
<script src="/funenc_driver/static/js_res/webuploader/webuploader.min.js"></script>
<style>
.webuploader-pick{
background-color: #5A6169;
}
</style>
</head>
<body>
<div class="container">
<div class="page-header">
<h3>大文件上传控件</h3>
</div>
<div class="panel panel-default">
<div class="panel-body">
<div>
<div id="picker" style="margin:20px 0">请选择文件</div>
<div id="progress" class="progress">
<div class="progress-bar progress-bar-striped active" role="progressbar"></div>
</div>
<form>
<input type="hidden" name="csrf_token" value="{{object.csrf_token}}">
<input type="hidden" name="res_model" value="{{object.res_model}}">
<input type="hidden" name="res_id" value="{{object.res_id}}">
</form>
<div style="clear:both"></div>
</div>
<table class="table table-bordered table-condensed">
<tr>
<th>附件名称</th>
<th>类型</th>
<th>大小(kb)</th>
</tr>
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
var res_model = $("input[name='res_model']").val();
var res_id = $("input[name='res_id']").val();

var task_id = WebUploader.Base.guid();
var uploader = WebUploader.create({
swf: "/funenc_driver/static/js_res/webuploader/Uploader.swf",
server: "/funenc_driver/file_upload/file_chunk",
pick: "#picker",
auto: true,//选择文件后自动上传
chunked: true,//是否分片
chunkSize: 25 * 1024 * 1024,//分片大小
chunkRetry: 3,//若某分片上传失败,重试次数
threads: 1,//线程数
duplicate: true,//分片是否自动去重
formData: { //上传分片,带的参数
task_id: task_id,
res_model: res_model,
res_id: res_id,

}
})
//开始上传调用方法
uploader.on("startUpload", function () {
$("#progress").show();
$(".progress-bar").css('width', '0%');
$(".progress-bar").text("0%");
$(".progress-bar").removeClass("progress-bar-danger progress-bar-success");
$(".progress-bar").addClass("active progress-bar-striped");
})
//进度条
uploader.on("uploadProgress", function (file, percent) {
$(".progress-bar").css("width", percent * 100 - 1 + '%');
$(".progress-bar").text(Math.floor(percent * 100 - 1) + '%');
})
//整个文件上传成功
uploader.on("uploadSuccess", function (file, response) {
var data = {
'res_model': res_model,
'res_id': res_id,
'task_id': task_id,
'filename': file.source['name'],
};
var tr = $("<tr><td>" + file.name + "</td><td>" + file.ext + "</td><td>" + Math.floor(file.size/1024) + "</td><tr>")
$("table").append(tr);
//合并分片
$.ajax({
url: '/funenc_driver/file_upload/file_merge',
type: 'post',
data: data,
success: function (res) {
var jres = JSON.parse(res);
if (jres.isOk) {
$(".progress-bar").css("width", "100%");
$(".progress-bar").text("100%");
$(".progress-bar").addClass("progress-bar-success");
$(".progress-bar").text("上传完成!");
} else {
$(".progress-bar").css("width", "100%");
$(".progress-bar").text("100%");
$(".progress-bar").addClass("progress-bar-danger");
$(".progress-bar").text("上传失败!");
}
},
error: function () {
$(".progress-bar").css("width", "100%");
$(".progress-bar").text("100%");
$(".progress-bar").addClass("progress-bar-danger");
$(".progress-bar").text("上传失败!");
}
}
)
})
//上传异常
uploader.on("uploadError", function (file) {
$(".progress-bar").css("width", "100%");
$(".progress-bar").text("100%");
$(".progress-bar").addClass("progress-bar-danger");
$(".progress-bar").text("上传失败!");
})
})
</script>
</body>
</html>
后端相关代码:
import base64
import logging
import zipfile
import io
import os
import json
from jinja2 import PackageLoader, Environment

env = Environment(loader=PackageLoader('yong.funenc_driver', 'static/html/webuploader'))
_logger = logging.getLogger(__name__)
base_dir = os.path.dirname(__file__)

from odoo import http
from odoo.http import request


class BigFileUpload(http.Controller):
"""
大文件上传(前端分片上传)
"""

@http.route("/funenc_driver/file_upload/index", type="http", auth="public", csrf=False)
def big_file_index(self, **kwargs):
"""
前端首页
:param kwargs:
:return:
"""
cr, uid, context, pool = request.cr, request.session.uid, request.context, request.env
values = dict()
if kwargs:
data = pool[kwargs["res_model"]].browse(int(kwargs["res_id"]))
values["object"] = data
values["csrf_token"] = None
values["res_model"] = kwargs["res_model"]
values["res_id"] = kwargs["res_id"]

template = env.get_template('index.html')
return template.render(object=values)
else:
return "异常!"

@http.route("/funenc_driver/file_upload/file_chunk", methods=["POST"], type="http", auth="public", csrf=False)
def file_chunk_upload(self, **kwargs):
"""
分片上传
:param kwargs:
:return:
"""
task = kwargs["task_id"]
chunk = request.params.get("chunk", '0')

filename = task + chunk

upload_file = kwargs["file"]
upload_file.save(base_dir + "/tem_files/" + filename)

target_filename = kwargs["name"]

return target_filename

@http.route("/funenc_driver/file_upload/file_merge", methods=["POST"], type="http", auth="public", csrf=False)
def file_chunk_merge(self, **kwargs):
"""
文件合并
:param kwargs:
:return:
"""
cr, uid, context, pool = request.cr, request.session.uid, request.context, request.env
target_filename = kwargs["filename"]
task = kwargs["task_id"]

chunk = 0
with open(base_dir + "/tem_files/" + target_filename, 'wb') as target_file:
while True:
filename = base_dir + "/tem_files/%s%d" % (task, chunk)
if os.path.exists(filename):
source_file = open(filename, 'rb')
target_file.write(source_file.read())
source_file.close()

chunk += 1
os.remove(filename)
else:
break

filename = base_dir + "/tem_files/" + target_filename
if os.path.exists(filename):
source_file = open(filename, 'rb')
file = source_file.read()
attachment_vals = {
'file_name': target_filename,
'file_data': base64.b64encode(file),
}
res = pool[kwargs["res_model"]].browse(int(kwargs["res_id"])).sudo().write(attachment_vals)
source_file.close()
os.remove(filename)
if res:
return json.dumps({
'isOk': True,
'msg': '上传成功!'
})
else:
return json.dumps({
'isOk': False,
'msg': '上传失败!'
})
else:
_logger.warning("%s 上传失败!" % filename)
return json.dumps({
'isOk': False,
'msg': '上传失败!'
})
相关效果:





前端接收64位数据流并导出文件
True