下面代码为 上传页
。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>Index</h2>
<div id="uploader" class="wu-example">
<!--用来存放文件信息-->
<div class="filename"></div>
<div class="state"></div>
<!-- <div class="progress">
<div class="progress-bar progress-bar-info progress-bar-striped active" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
<span class="sr-only">40% Complete (success)</span>
</div>
</div> -->
<div class="btns">
<div id="picker">选择文件</div>
<button id="ctlBtn" class="btn btn-default">开始上传</button>
<button id="pause" class="btn btn-danger">暂停上传</button>
</div>
</div>
<script src="./static/jquery-3.3.1/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(function () {
var GUID = WebUploader.Base.guid();//一个GUID
var uploader = WebUploader.create({
swf: './static/webuploader/Uploader.swf',
server: 'http://47.103.209.62:8080/upload/run',
pick: '#picker',
resize: false,
chunked: true,//开始分片上传
chunkSize: 2048000,//每一片的大小
formData: {
guid: GUID //自定义参数,待会儿解释
}
});
uploader.on('fileQueued', function (file) {
$("#uploader .filename").html("文件名:" + file.name);
$("#uploader .state").html('等待上传');
});
uploader.on('uploadSuccess', function (file, response) {
$.post('http://47.103.209.62:8080/upload/complete', { guid: GUID, fileName: file.name }, function (data) {
$list.text('已上传');
});
});
uploader.on('uploadProgress', function (file, percentage) {
$("#uploader .progress-bar").width(percentage * 100 + '%');
console.log(percentage);
});
uploader.on('uploadSuccess', function () {
$("#uploader .progress-bar").removeClass('progress-bar-striped').removeClass('active').removeClass('progress-bar-info').addClass('progress-bar-success');
$("#uploader .state").html("上传成功...");
});
uploader.on('uploadError', function () {
$("#uploader .progress-bar").removeClass('progress-bar-striped').removeClass('active').removeClass('progress-bar-info').addClass('progress-bar-danger');
$("#uploader .state").html("上传失败...");
});
$("#ctlBtn").click(function () {
uploader.upload();
$("#ctlBtn").text("上传");
$('#ctlBtn').attr('disabled', 'disabled');
$("#uploader .progress-bar").addClass('progress-bar-striped').addClass('active');
$("#uploader .state").html("上传中...");
});
$('#pause').click(function () {
uploader.stop(true);
$('#ctlBtn').removeAttr('disabled');
$("#ctlBtn").text("继续上传");
$("#uploader .state").html("暂停中...");
$("#uploader .progress-bar").removeClass('progress-bar-striped').removeClass('active');
});
});
</script>
<link href="./static/webuploader/webuploader.css" rel="stylesheet" />
<script src="./static/webuploader/webuploader.nolog.js"></script>
</body>
</html>
下面代码为 分片上传Api
。
//分片上传
func (this *UploadController) RunMultipartUpload() {
//参数
data, err := this.GetParam()
if err!=nil{
code,_ := beego.AppConfig.Int("systemError")
this.ErrorJson(code, err.Error())
}
//接收字段名
field_name := "file"
//获取文件
f, h, err := this.GetFile(field_name)
if err != nil {
code,_ := beego.AppConfig.Int("systemError")
this.ErrorJson(code, err.Error())
}
defer f.Close()
//保存文件路径
dir := "static/multipart-upload/"+data["guid"].(string)
//检查是否存在文件夹,没有则创建
err = common.PathExists(dir)
if err!=nil{
code,_ := beego.AppConfig.Int("systemError")
this.ErrorJson(code, err.Error())
}
//文件后缀
fileExt := path.Ext(h.Filename)
//保存文件名
fileName := data["chunk"].(string) + fileExt
//完整路径
path := dir +"/"+ fileName
//保存文件
err = this.SaveToFile(field_name, path)
if err!=nil{
code,_ := beego.AppConfig.Int("systemError")
this.ErrorJson(code, err.Error())
}
//返回
res := make(map[string]interface{})
res["path"] = path
this.SuccessJson(res)
}
下面代码为 通知文件合并Api
。
//完成上传通知文件合并
func (this *UploadController) CompleteUpload() {
//参数
data, err := this.GetParam2()
if err!=nil{
code,_ := beego.AppConfig.Int("systemError")
this.ErrorJson(code, err.Error())
}
t := time.Now()
//分片目录
srcPath := "static/multipart-upload/"+data["guid"].(string)
//合并后保存文件目录
destPath := "static/upload/"+t.Format("2006-01-02")
//检查是否存在文件夹,没有则创建
err = common.PathExists(destPath)
if err!=nil{
code,_ := beego.AppConfig.Int("systemError")
this.ErrorJson(code, err.Error())
}
//文件后缀
fileSuffix := path.Ext(data["fileName"].(string))
//文件名
fileName := data["guid"].(string) + fileSuffix
//命令行
cmd := fmt.Sprintf("cd %s && ls | sort -n | xargs cat > $GOPATH/src/beegoApp/%s/%s", srcPath, destPath, fileName)
fmt.Print(cmd)
//执行命令行
mergeRes, err := common.ExecLinuxShell(cmd)
if err!=nil{
code,_ := beego.AppConfig.Int("systemError")
this.ErrorJson(code, err.Error())
}
//返回
res := make(map[string]interface{})
res["mergeRes"] = mergeRes
this.SuccessJson(res)
}
前端使用webuploader
插件的分片上传,把大文件分成一小片一小片上传到服务端(请求分片上传Api
),服务端拿到分片文件先把他们临时存储起来,用前端传过来的guid
参数作为分片临时目录名,chunk
参数作为分片文件名以方便排序。
最后上传完成前端会通知服务端合并文件(请求通知文件合并Api
),服务端会把分片目录里面的所有分片文件通过文件名进行排序,并合并成为一个大文件。
优点:文件是一点一点上传到服务端的,实现了断点续传,再也不用担心掉线问题了。
缺点:前端要不断的请求上传接口,服务端压力大。
PS:此代码只是一个大概的思路,一些功能,如前端的上传进度条,上传记录的持久化,文件合并之后删除分片目录等,都可以逐一完善,有什么建议都欢迎评论区讨论。