Spring MVC+MediaElement.js实现在WEB上播放MP4并支持拖移播放
来源:互联网 发布:android c 高级编程 编辑:程序博客网 时间:2024/06/06 19:30
导语
使用MediaElement.js,在WEB上可以播放MP4文件,功能强大,定制性强,支持拖移播放。若文件在工程目录下,则可以直接使用tomcat的DefaultServlet来处理,则MediaElement.js可以完美播放,若是自定义的,则继续往下看。
原理
播放器是采用分块请求下载的,断点续传的方式,其原理如下:
http协议中,服务端实现断点续传首先需要读取客户端传送的Range头信息,比如“Range: bytes=12583394-”这个就是指原来正在下载的文件需要从第12583394字节继续下载,然后我们利用Java.io.File的skip方法,舍弃掉原文件的前n个字节,接着就继续慢慢write吧。。。
但是客户端又是如何判断服务端是否支持断点续传的呢?主要就是Accept-Ranges和Content-Length头信息。比如“Accept-Ranges:bytes”和“Content-Length:99999999”。有了这两个头信息,客户端就认为服务端是支持断点续传的了。
然后需要注意的是,假如客户端刚才由于某些原因,暂停了下载,现在恢复的时候,就会如前所述,传来Range头信息,这时候,我们的response就需要设置一下状态码,这里应该设置成206(详细解释请看http://en.wikipedia.org/wiki/List_of_HTTP_status_codes),还有就是Content-Range头信息,格式为“bytes x-(y-1)/y”,x就是客户端传来的开始字节位置,y就是文件长度。
实现
1.JSP页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><link rel="stylesheet" href="vender/mediaelement/mediaelementplayer.css"></head><body style="width: 100%; height: 100%;"> <div class=warp"> <div style="text-align: center;margin-top: 10px"> <h2>${videoTitle}</h2> </div> <div style="margin: 0 auto;width: 1280px" data-options="border:false"> <video src="${videoUrl}" width="1280" height="720" data-mejsoptions='{"stretching" : "auto","pluginPath" : "vender/mediaelement/", "alwaysShowControls": "true", "lang" : "zh-cn"}'></video> </div> </div> <script src="vender/jquery.js"></script> <script src="vender/mediaelement/mediaelement-and-player.js"></script> <script src="vender/mediaelement/lang/zh-cn.js"></script> <script type="text/javascript"> // 设置语言为中文 mejs.i18n.language('zh-CN'); // 创建播放器 $('video').mediaelementplayer({ stretching : "auto", pluginPath : "vender/mediaelement/", alwaysShowControls : true, success : function(player, node) { player.play(); } }); </script></body></html>
2.Spring MVC的Controller
/** * 大文件分块下载 * * @param request * @param response * @throws IOException */ @RequestMapping("/storage/**") public void bigFileDownload(HttpServletRequest request, HttpServletResponse response) throws IOException { String uri = URLDecoder.decode(request.getRequestURI(), "UTF-8"); String filename = uri.substring(uri.indexOf(STORAGE) + STORAGE.length() + 1, uri.length()); File downloadFile = fileService.getFile(filename); // 要下载的文件 long fileLength = downloadFile.length();// 记录文件大小 long pastLength = 0;// 记录已下载文件大小 long toLength = 0;// 记录客户端需要下载的字节段的最后一个字节偏移量(比如bytes=27000-39000,则这个值是为39000) long contentLength = 0;// 客户端请求的字节总量 String rangeBytes = "";// 记录客户端传来的形如“bytes=27000-”或者“bytes=27000-39000”的内容 // ETag header // The ETag is contentLength + lastModified response.setHeader("ETag", "W/\"" + fileLength + "-" + downloadFile.lastModified() + "\""); // Last-Modified header response.setHeader("Last-Modified", new Date(downloadFile.lastModified()).toString()); if (request.getHeader("Range") != null) {// 客户端请求的下载的文件块的开始字节 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); log.info("request.getHeader(\"Range\")=" + request.getHeader("Range")); rangeBytes = request.getHeader("Range").replaceAll("bytes=", ""); if (rangeBytes.indexOf('-') == rangeBytes.length() - 1) {// bytes=969998336- rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf('-')); pastLength = Long.parseLong(rangeBytes.trim()); toLength = fileLength - 1; } else {// bytes=1275856879-1275877358 String temp0 = rangeBytes.substring(0, rangeBytes.indexOf('-')); String temp2 = rangeBytes.substring( rangeBytes.indexOf('-') + 1, rangeBytes.length()); // bytes=1275856879-1275877358,从第 1275856879个字节开始下载 pastLength = Long.parseLong(temp0.trim()); // bytes=1275856879-1275877358,到第 1275877358 个字节结束 toLength = Long.parseLong(temp2); } } else {// 从开始进行下载 toLength = fileLength - 1; } // 客户端请求的是1275856879-1275877358 之间的字节 contentLength = toLength - pastLength + 1; if (contentLength < Integer.MAX_VALUE) { response.setContentLength((int) contentLength); } else { // Set the content-length as String to be able to use a long response.setHeader("content-length", "" + contentLength); } WebApplicationContext webApplicationContext = ContextLoader .getCurrentWebApplicationContext(); ServletContext servletContext = webApplicationContext .getServletContext(); String contentType = servletContext.getMimeType(filename); if (null != contentType) { response.setContentType(contentType); } // 告诉客户端允许断点续传多线程连接下载,响应的格式是:Accept-Ranges: bytes response.setHeader("Accept-Ranges", "bytes"); // 必须先设置content-length再设置header response.addHeader("Content-Range", "bytes " + pastLength + "-" + toLength + "/" + fileLength); response.setBufferSize(2048); InputStream istream = null; OutputStream os = null; try { os = response.getOutputStream(); istream = new BufferedInputStream( new FileInputStream(downloadFile), 2048); try { copyRange(istream, os, pastLength, toLength); } catch (IOException ie) { /** * 在写数据的时候, 对于 ClientAbortException 之类的异常, * 是因为客户端取消了下载,而服务器端继续向浏览器写入数据时, 抛出这个异常,这个是正常的。 * 尤其是对于迅雷这种吸血的客户端软件, 明明已经有一个线程在读取 bytes=1275856879-1275877358, * 如果短时间内没有读取完毕,迅雷会再启第二个、第三个。。。线程来读取相同的字节段, 直到有一个线程读取完毕,迅雷会 KILL * 掉其他正在下载同一字节段的线程, 强行中止字节读出,造成服务器抛 ClientAbortException。 * 所以,我们忽略这种异常 */ // ignore } } catch (Exception e) { log.error(e.getMessage(), e); } finally { if (istream != null) { try { istream.close(); } catch (IOException e) { log.error(e.getMessage(), e); } } } } protected void copyRange(InputStream istream, OutputStream ostream, long start, long end) throws IOException { long skipped = 0; skipped = istream.skip(start); if (skipped < start) { throw new IOException("skip fail: skipped=" + Long.valueOf(skipped) + ", start=" + Long.valueOf(start)); } long bytesToRead = end - start + 1; byte buffer[] = new byte[2048]; int len = buffer.length; while ((bytesToRead > 0) && (len >= buffer.length)) { try { len = istream.read(buffer); if (bytesToRead >= len) { ostream.write(buffer, 0, len); bytesToRead -= len; } else { ostream.write(buffer, 0, (int) bytesToRead); bytesToRead = 0; } } catch (IOException e) { len = -1; throw e; } if (len < buffer.length) break; } }
阅读全文
0 0
- Spring MVC+MediaElement.js实现在WEB上播放MP4并支持拖移播放
- HTML5播放器MediaElement.js用法
- 在web上播放音乐
- 在 Web 上播放视频
- Android 开关机动画的实现及支持MP4视频播放
- 【WPF知识积累】MediaElement在windows7上双显示器播放的问题
- 关于MP4视频在浏览器上无法播放的问题(没有找到支持的视频格式和mime类型)
- [基础]MPMoviePlayerViewController实现MP4播放
- mp4播放
- uwp开发:Slider控件和MediaElement绑定,实现拖动播放.
- WPF MediaElement 声音循环播放
- MediaElement的视频循环播放
- Wpf中MediaElement循环播放
- WF MediaElement 视频播放器
- 下载mp4视频到本地,并播放
- ubuntu 14.04安装live555并支持MP4格式播放安装过程
- krpano调用js方法并传参、在krpano中用JS实现视频音频播放
- ckplayer实现mp4等html5支持格式视频的网页播放
- STM32学习笔记:实验
- Gogland同步插件配置
- Fle.createNewFile() 和 Fle.createTempFile() 比较和区别
- Unity计算速率的简单方法
- Java如何实现调用oracle带有返回值的存储过程
- Spring MVC+MediaElement.js实现在WEB上播放MP4并支持拖移播放
- List集合转成String字符串
- 请求被中止: 未能创建 SSL/TLS 安全通道
- jQuery报错:Uncaught ReferenceError: $ is not defined
- 剑指offer——37.数字在排序数组中出现的次数
- finereport自带的模板目录
- 数学基础01
- python 可迭代对象与迭代器
- Sublime-HTMLPrettify