大文件分片上传的大概思路(go语言)

前端代码

下面代码为 上传页

<!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:此代码只是一个大概的思路,一些功能,如前端的上传进度条,上传记录的持久化,文件合并之后删除分片目录等,都可以逐一完善,有什么建议都欢迎评论区讨论。

猜你喜欢

转载自blog.csdn.net/qq_36453564/article/details/107230235