一、前言
关于这边文章,一直都想写上去来做记录,终于今晚是看了《我想我们在一起》这电影之后,触动而写。 有些事情还是不要等待,能当下做就赶紧做,不然都是给自己遗憾的。 回归正题,说的是断点上传,需要把文件切割成片。下面是我个人的大概思路,也借鉴了百度一些前辈们留下的经验而成(说借鉴其实差不多就是ctrl+c 和ctrl+v 的问题)。
二、前端代码
只是提供一个分片思路。 个人感觉下面js 代码标红部分是比较重要的吧。
<input class="easyui-filebox" name="filevideo" id="filevideo" labelPosition="top" data-options="prompt:'请选择视频',buttonText:'选择',accept:'video/avi,audio/mp4, video/mp4,video/mpeg,video/avi'">
var bytesPerPiece = 1024*1024 ; // 每个文件切片大小定为1MB . var totalPieces; //发送请求 window.uploads = function (obj) { var blob =$("#filevideo").filebox("files")[0];//1.获取文件对象 var start = 0; var end; var index = 0;//索引片数 var filesize = blob.size;//2.文件大小 var filename = blob.name;//3.文件名称带后缀名 if (filename.indexOf(".mp4") == -1) { $.messager.alert('提示', '目前只支持上传mp4格式'); return; }
//计算文件切片总数 totalPieces = Math.ceil(filesize / bytesPerPiece); //4.计算分片总数 var guid= '@Guid.NewGuid().ToString()';//这是我自定义的,以GUID 命名一个临时文件夹
//5.开始分片 while (start < filesize) { end = start + bytesPerPiece; if (end > filesize) { end = filesize; } var chunk = blob.slice(start, end);//6.切割文件 var formData = new FormData();//7.用FormData 附带参数 上传文件 formData.append("file", chunk, filename); formData.append("fileName", fid); formData.append("totalPieces", totalPieces); formData.append("currentPieces", index + 1); formData.append("guid", guid); $.ajax({ url: '@Url.Content("~/Admins/Video/UploadFile")', type: 'POST', cache: false,//这个很重要 data: formData, processData: false,//这个很重要 contentType: false, dataType:'json', }).done(function (result) {
//这一句防止重复调用 if (proceseBarVal == 100) { return; } if (result.isuccess)//是否上传成功 { if (servercurent.currentPieces != -1) { //这里是计算当前进度 } else { //完成上传 //执行合并接口 $.ajax({ url: '@Url.Content("~/Admins/Video/MeterFile")', type: 'POST', cache: false, data: data, dataType: "json", success: function (res) { if (res.success) { } else { $.messager.alert("提示", res.msg) } } }); } } }).fail(function (res) { }); start = end; index++; } }
三、后端代码
看这代码还是有点混乱的,没有做整理。 后续附加一个demo出来比较好。
/// <summary> /// 文件上传 /// </summary> /// <param name="fileName">临时文件夹名称</param> /// <param name="totalPieces"></param> /// <param name="currentPieces"></param> /// <returns></returns> [HttpPost] public async Task<string> UploadFile(string fileName, string totalPieces, string currentPieces,string guid) { ResultData rmodel = new ResultData(); string ID = Guid.NewGuid().ToString(); try { var filebase = Request.Form.Files; string webRootPath = _hostingEnvironment.WebRootPath; string virRoot = "/files/video/temp/" + UserName+ "/" + DateTime.Now.ToString("yyyyMMdd") + "/" + guid + "/";//虚拟路径,用于存数据库 if (!Directory.Exists(webRootPath + virRoot)) Directory.CreateDirectory(webRootPath + virRoot); if (filebase.Count > 0) { for (int i = 0; i < filebase.Count; i++) { var file = filebase[i]; virRoot = virRoot + currentPieces; var filePath = webRootPath + virRoot; using (var stream = new FileStream(filePath, FileMode.Create)) { await file.CopyToAsync(stream).ConfigureAwait(false); } if (totalPieces == currentPieces) { string extName = FileHelper.GetFileExtendName(filebase[i].FileName); //上传完毕之后,就直接合并! rmodel.data = new { dir = webRootPath + "/files/video/temp/" + UserName + "/" + DateTime.Now.ToString("yyyyMMdd") + "/" + guid + "/", exName = Guid.NewGuid().ToString() + extName, currentPieces = -1 //MeterFile(webRootPath + "/files/video/temp/" + UserName + "/" + DateTime.Now.ToString("yyyyMMdd") + "/" + guid+ "/", Guid.NewGuid().ToString() + extName), }; rmodel.isuccess = true; rmodel.msg = "上传成功!"; } else { rmodel.isuccess = true; rmodel.data = new { currentPieces = currentPieces }; } } } } catch (Exception ex) { rmodel.msg = ex.Message; } return rmodel.ToJson(); } /// <summary> /// 合并文件 /// </summary> [HttpPost] public AjaxResult MeterFile(string dir, string exName) { AjaxResult result = new AjaxResult() { Success=false}; try { var files = System.IO.Directory.GetFiles(dir);//获得下面的所有文件 string localhost = dir.Replace("temp/" + UserName + "/", ""); if (!Directory.Exists(localhost)) Directory.CreateDirectory(localhost); var fs = new FileStream(localhost + exName, FileMode.Create); foreach (var part in files.OrderBy(x => x.Length).ThenBy(x => x))//排一下序,保证从0-N Write { var bytes = System.IO.File.ReadAllBytes(part); fs.Write(bytes, 0, bytes.Length); bytes = null; System.IO.File.Delete(part);//删除分块 } fs.Close(); //System.IO.Directory.Delete(dir);//删除文件夹 result.Msg = "上传成功"; result.Success = true; result.Data = new { url = Path.Combine(dir.Replace(_hostingEnvironment.WebRootPath, "").Replace("temp/" + UserName + "/", ""), exName) }; } catch (Exception ex) { LogHelper.WriteLog_LocalTxt(ex.Message); result.Msg = "合并失败,请联系管理员!"; } return result; }
四、结束
在此代码之前,我是直接把文件切片上传之后,马上合并,导致一个问题就是,文件没有上传完成马上执行合并方法,1.删除文件夹报错 2.合并的视频无法播放或者播放不全。 后来跟同事讨论了一下,把文件上传完成之后,再请求合并接口(注意,必须要设置同步,异步的话会出现文件未上传完成就进行合并了)。我上面只是提供一个上传文件思路,并不是最优的代码。所以进行优化。