1、先看最终实现效果:
开始:
保存中:
保存结束:还在本页面,同时有提示,上面已经更新了内容。
具体实现步骤:
1、由于我使用了框架内置对象处理二进制文件信息,所有接收过程完全被封闭在框架内置对象中,直到请求结束才返回信息提示,上传的过程进度无法访问。我使用了js,不通过form表单action跳转后台上传。
(1)html内容如下;测试期间只需要关注那几个上传的字段和按钮·就好了,我们在js中只需要用到字段id获得信息,通过js上传。其他的都是布局和样式。
<!--上传课节部分--> <div class="row"> <!-- /.col --> <div class="col-md-12"> <div class="nav-tabs-custom"> <div class="tab-content box-body"> <h4 class="text-center">上传课节</h4> <!--<form class="form-horizontal" action="/savenewcoursecontent" method="post" id="myform" enctype="multipart/form-data">--> <div class="form-group"> <input type="hidden" name="courseid" id="courseid" th:value="${course.getCourseid()}"> <label class="col-sm-2 control-label">课节名称</label> <div class="col-sm-10"> <input type="text" class="form-control" placeholder="请输入课节名称" name="contentname" id="contentname"> <span></span> </div> </div> <div class="form-group"> <p></p> <label class="col-sm-2 control-label">课节资源</label> <div class="col-sm-10"> <input type="file" name="img" th:id="file" th:onchange="checkimgaaa()"><span></span><br> <div class="col-lg-5" style="padding-left: 0; padding-right: 0; margin-bottom: 0px;"> <div class="progress progress-striped active" style="display:"> <div id="progressBar" class="progress-bar progress-bar-success" role="progressbar" aria-valuemin="0" aria-valuenow="0" aria-valuemax="100" style="width: 0%"></div> </div> </div> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">章节名称</label> <div class="col-sm-10"> <select name="chapterid" th:id="chapterid"> <option th:each="u:${chapternames}" th:value="${u.chapterid}" th:text="${u.chaptername}"> </option> </select> </div> </div> <div class="form-group" style="text-align: center"> <div class="col-sm-offset-2 col-sm-12"> <button type="submit" class="btn btn-primary " name="submit" id="mysave">提交保存</button> <button type="button" name="qv" id="cancal" style="display: none"></button> <button type="button" class="btn btn-primary " onclick="end()">上传结束</button> </div> </div> <!--</form>--> <!-- /.tab-pane --> </div> <!-- /.tab-content --> </div> <!-- /.nav-tabs-custom --> </div> <!-- /.col --> </div>
(2)导入封装的js文件,内容如下:需要根据需要进行调整和修改上传成功和失败后自定义的操作以及参数内容,后面会提到。代码意思自行理解。代码如下:
/** * 上传文件公共组件 * * @param url 上传地址 * @param processBar 进度条 jquery获取的页面组件 * @param speedLab 显示上传速度Label jquery获取的页面组件 * @param uploadBtn 上传按钮 jquery获取的页面组件 * @param cancelBtn 取消上传按钮 jquery获取的页面组件 * @param callBack 上传完成回调函数 上传完成后的回调函数,可以不传 * @author * @returns */ function UploadCommon(url, processBar, speedLab, uploadBtn, cancelBtn,courseid, callBack) { function init() { // 每次回调监测上传开始时间 var startTime = null // 已上传文件大小 var oloaded = null var xhr = new XMLHttpRequest() function setProgress(w) { processBar.width(w + '%'); processBar.text(w + '%'); } function progressMonitor(evt) { if (evt.lengthComputable) { var completePercent = Math.round(evt.loaded / evt.total * 100) setProgress(completePercent) var nowTime = new Date().getTime() // 计算出上次调用该方法时到现在的时间差,单位为s var pertime = (nowTime - startTime) / 1000 // 重新赋值时间,用于下次计算 startTime = new Date().getTime() // 计算该分段上传的文件大小,单位b var perload = evt.loaded - oloaded // 重新赋值已上传文件大小,用以下次计算 oloaded = evt.loaded // 上传速度计算,单位b/s var speed = perload / pertime var bspeed = speed // 单位名称 var units = 'bit/s' if (speed / 1024 > 1) { speed = speed / 1024 units = 'Kb/s' } if (speed / 1024 > 1) { speed = speed / 1024 units = 'Mb/s' } speed = speed.toFixed(1); // 剩余时间 var resttime = ((evt.total - evt.loaded) / bspeed).toFixed(1) speedLab.html(speed + units + ',剩余时间:' + resttime + 's') } } // 上传成功后回调 function uploadComplete(evt) { uploadBtn.attr('disabled', false) var status = evt.currentTarget.status if (status == 401) { alert('请登录后再上传') return } if (status == 403) { alert('无权限操作') return } if (status != 200) { alert('上传异常,错误码' + status) return } var response = JSON.parse(evt.currentTarget.response); if (response != 1) { alert(1000) }else{ window.location.href="/tosavecontentname?i=1"+ "&courseid=" + courseid; } if (callBack != null && typeof callBack != 'undefined') { callBack() } }; // 上传失败回调 function uploadFailed(evt) { alert('上传处理失败' + evt.target.responseText) } // 终止上传 function cancelUpload() { xhr.abort() } // 上传取消后回调 function uploadCanceled(evt) { alert('文件上传已终止:' + evt.target.responseText) } // 添加取消上传事件 cancelBtn.click(function () { uploadBtn.attr('disabled', false) cancelUpload(); }) this.uploadFile = function (formData) { // 上传按钮禁用 uploadBtn.attr('disabled', true); setProgress(0) // true为异步处理 xhr.open('post', url, true) // 上传开始执行方法 xhr.onloadstart = function () { console.log('开始上传') // 设置上传开始时间 startTime = new Date().getTime() // 已上传的文件大小为0 oloaded = 0 } xhr.upload.addEventListener('progress', progressMonitor, false) xhr.addEventListener("load", uploadComplete, false) xhr.addEventListener("error", uploadFailed, false) xhr.addEventListener("abort", uploadCanceled, false) xhr.send(formData); } this.setProgressValue = function (w) { processBar.width(w + '%') processBar.text(w + '%') } } return new init() }
(3)在html页面调用该组件,并根据自己操作的字段进行设置。具体含义代码中介绍。
<script type="text/javascript"> $(document).ready(function() { var addVersionBtn=$('#mysave') //<button>//点击上传的按钮 var cancelUploadBtn=$('#cancal') //<button>//取消的按钮,这次我没用 var fileInput=$("#file") //<input>//获得文件的id var processBar = $("#progressBar"); //获得显示的进度条的id var speedLab=$("#showInfo") //<label> var courseid=$('#courseid').val();//我在上面的js组件中需要这个字段,在成功后进行跳转,所以给组件加了参数,在commons.js中加了 var url='/savenewcoursecontent'//我后台上传内容的地址。 //获取文件上传实例,调用组件 var upload=UploadCommon(url,processBar,speedLab,addVersionBtn,cancelUploadBtn,courseid); // 点击上传按钮后触发的事件 addVersionBtn.click(function(){ //获得这些需要上传字段的数据 var courseid=$('#courseid').val() var file = fileInput.get(0).files[0] var contentname=$('#contentname').val() var chapterid=$('#chapterid').val() //对字段的内容进行判断,限制内容的格式等 if(file==null){ alert("视频文件不能为空") return } if(courseid==''){ alert("courseid不能为空"); return } if(contentname==''){ alert("课程名字不能为空") return } if(chapterid==''){ alert("章节名不能为空") return } // 创建提交数据,将数据添加到formData中,然后上传 var formData = new FormData(); formData.append('img', fileInput.get(0).files[0]); formData.append('courseid', courseid); formData.append('contentname',contentname); formData.append('chapterid', chapterid); // 上传文件 upload.uploadFile(formData) }) }); </script>
我上次的是视频文件,所以加了下面的代码进行限制mp4格式,也可以在上面对字段进行判断中添加。
//对上传的视频文件类型进行限制 function checkimgaaa() { var aa = document.getElementById("file").value.toLowerCase().split("."); /*以.分割文件字符串*/ if (aa[aa.length - 1] == 'mp4') { } else { document.getElementById("file").value = ''; alert("格式不对,只能上传.mp4的视频"); } }
2、下面进行后台处理。由于我是使用spring boot +thymeleaf+Mysql数据库进行开发,其他开发可能不一样。仅供参考
/** * 保存新增的课节信息 */ @RequestMapping(value = "/savenewcoursecontent", method = RequestMethod.POST) @ResponseBody public int aasavechapteradfad(MultipartFile img, String contentname, String chapterid, String courseid) { ModelAndView m = new ModelAndView(); Date date = new Date(); SimpleDateFormat sd1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String contenttime = sd1.format(date); //保存课程图片 String filename = null; String path = ClassUtils.getDefaultClassLoader().getResource("").getPath() + "static/video"; //给图片命名简单随机 Random random = new Random(); filename = System.currentTimeMillis() + random.nextInt(100000) * 100 + img.getOriginalFilename(); File f = new File(path, filename); if (!f.exists()) { try { f.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } try { img.transferTo(f); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //更新数据库的图片字段 String contentpath = "/video/" + filename; CourseContent courseContent = new CourseContent(); courseContent.setChapterid(Integer.valueOf(chapterid)); courseContent.setCourseid(Integer.valueOf(courseid)); courseContent.setContentname(contentname); courseContent.setContenttime(contenttime); courseContent.setContenturl(contentpath); //将课节保存进去数据库 int i = cs.savecoursecontent(courseContent); return i; }
(2)我们可以根据效果演示看到,成功后还在本页面,而且提示了上传成功。那是我们上传完成后,返回了int类型的i,在上面提到的组件中,进行了成功的跳转。重复刚才组件中的这行。返回1,就直接进行跳转,带着我们第一次到此页面的内容和该值1。
var response = JSON.parse(evt.currentTarget.response); if (response != 1) { alert("上传失败") }else{ window.location.href="/tosavecontentname?i=1"+ "&courseid=" + courseid; }
在这个上传课节的页面最下面有我写了js,来接收i的值,并进行alert,没有值不响应,该内容必须在最下面,否则第一次进来不带i值,下面的js都无效了。
<!--页面独有的js--> var i=[[${i}]]; if(i==1){ alert("上传成功!"); }else { alert("保存失败!") }
返回0直接alert上传失败。如果其他页面也有上传文件可视化操作,我们可以在成功后返回不同的响应值,进行不同的跳转。失败都返回0,直接alert失败。当然跳转不同的页面需要不同的跳转数据,我们可以在组件方法参数上进行添加。不需要的在调用时赋值空就行了。