`
zy116494718
  • 浏览: 477795 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

视频库之断点续传

    博客分类:
  • Java
 
阅读更多

        近日公司有个项目要做一个视频库,故开始阶段性研究。首先先要研究断点续传。

        第一步:打算提高上传视频的时间,上传的时间分成两部分,读流和写入GFS。想做一个实验来验证到底时间花费在了哪里,故在程序中加入了打印语句。如下:

      

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		
		System.out.println("1="+sdf.format(new Date()));

		try {
			ServletFileUpload upload = new ServletFileUpload();
			upload.setFileSizeMax(fileSizeMax * 1024 * 1024);
			FileItemIterator iter = upload.getItemIterator(request);
			while(iter.hasNext()){
				FileItemStream item = iter.next();
				String fileName = item.getName();
				InputStream stream = item.openStream();
			
				String fn = item.getFieldName();
				System.out.println("2="+sdf.format(new Date()));
				if (!item.isFormField() && !StringUtil.isEmpty(fn)) {
					
					System.out.println("3(1)="+sdf.format(new Date()));
					String cloudFileName = videoCloudStorage.upload(fileName,stream);
					System.out.println("3(2)="+sdf.format(new Date()));
					out.write(ReturnMessageUtil.createOKMsg(cloudFileName));
				}
			}
		} catch (Exception e) {
			logger.error("上传文件失败!", e);
//			out.write(ReturnMessageUtil.createErrorMsg("upload failed!"));
			response.sendError(500, "上传文件失败!");
		}
         System.out.println("4="+sdf.format(new Date()));
	}

    打印结果为:

1=2015-07-29 17:50:13
2=2015-07-29 17:50:13
2=2015-07-29 17:50:13
33(1)=2015-07-29 17:50:13
33(2)=2015-07-29 17:50:19
2=2015-07-29 17:50:19
4=2015-07-29 17:50:19

 可以看出时间都花在了往云存储上传输。当时觉得客户端往weblogic传流是秒传的,weblogic是在得到了全部流后才往云存储上传输。然后又做实验,这次是不传云存储了,而是直接把流写文件到weblogic上,代码入下:

 

File file = new File("//data//weblogic//1.mp4");
					FileOutputStream fos = new FileOutputStream(file);
					BufferedOutputStream bos = new BufferedOutputStream(fos);

					BufferedInputStream bis = new BufferedInputStream(stream);
					try {
						byte[] buffer = new byte[2048];
						int len = -1;
						while ((len = bis.read(buffer)) != -1) {
							// 输出读入的数据
							bos.write(buffer, 0, len);
						}
						bos.close();

					} catch (IOException e) {
						e.printStackTrace();
					}

 

 

    这次再打印语句,发现传云存储和直接传服务器上生成文件时间基本一样,但是这个还是无法判断出来到底时间是花在了读取流上还是花在了写文件上,然后又做了一个实验,就是把获取的流不写入文件了,而是读取后什么都不做,直接注掉上面的bos.write()方法,再跑一遍,发现结果还是一样,所以只能得出的结论是,流并不是一次性就传到了weblogic上,而是只是打开了一个通道,当

 while ((len = bis.read(buffer)) != -1) {

    的时候才是去真正读取流,所以真正费时间还是在读取流上而非写入文件。但是FLASH端说只能单线程传流,所以看来优化上传还是要想别的办法。

  

      第二步:秒传

       用户点击上传按钮,FLASH先发送请求给后端,后端从数据库中用MD5判断验证是否已经上传过(且
上传完整)该视频,如果上传过,则实现秒传(即为直接返回服务器存储地址,不用再上传),如果上传过,但是没有上传全部,则给FLASH返回一个
已上传文件大小,FLASH重新分份后继续发送剩下的文件流给服务端。
       知识点:MD5生成散列码

       java.security.MessageDigest类用于为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。简单点说就是用于生成散列码信息摘要是安全的单向哈希函数,它接收任意大小的数据,输出固定长度的哈希值。

      用法如下:

     

public static void main(String[] args){
		  try {
			String str = "abcd";
			MessageDigest com=MessageDigest.getInstance("MD5"); //生成MessageDigest对象
			com.update(str.getBytes("UTF-8"));  //传入需要计算的字符串
			byte[] b = com.digest(); //计算消息摘要
			System.out.println(convertToHexString(b));
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	  }
	  
	  static String convertToHexString(byte data[]) {
		  StringBuffer strBuffer = new StringBuffer();
		  for (int i = 0; i < data.length; i++) {
		   strBuffer.append(Integer.toHexString(0xff & data[i]));
		  }
		  return strBuffer.toString();
      }

     输出结果为:e2fc714c4727ee9395f324cd2e7f331f  (固定长度的)

 

     如果是想获得文件的散列码,则代码为:

    

public class Auth{

	  public static byte[] create(String filename)   throws Exception{
	  InputStream fis = new FileInputStream(filename);
	  byte[] buf= new byte[1024];
	  MessageDigest com=MessageDigest.getInstance("MD5");
	  int num;
	  do{
	   num=fis.read(buf);
	   if(num>0){
	     com.update(buf,0,num);
	   }
	  }while(num!=-1);

	  fis.close();
	  return com.digest();
	  }

	  
	  public static void main(String[] args){
		  try {
			byte[] fileA =create("E:\\1.jpg");
			System.out.println(convertToHexString(fileA));		
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	  }
	  
	  static String convertToHexString(byte data[]) {
		  StringBuffer strBuffer = new StringBuffer();
		  for (int i = 0; i < data.length; i++) {
		   strBuffer.append(Integer.toHexString(0xff & data[i]));
		  }
		  return strBuffer.toString();
      }  
	}

     输出结果为:4bca3e9cec71f8ad3601ddd31d7ceb

 

     第三步:合并文件

     断点续传的核心就是分块上传,然后再进行合并,我们先来了解一下合并所需的类RandomAccessFile。

    

RandomAccessFile类是随机读取类,它是一个完全独立的类。

适用于由大小已知的记录组成的文件,所以我们可以使用seek()将记录从一处转移到另一处,然后读取或者修改记录。

文件中记录的大小不一定都相同,只要能够确定哪些记录有多大以及它们在文件中的位置即可。

RandomAccessFile类可以实现对文件内容的读写操作,但是比较复杂。所以一般操作文件内容往往会使用字节流或字符流方式
(1)写入数据
当用 rw 方式声明RandomAccessFile对象时,如果要写入的文件不存在,系统将自行创建。 
r 为只读;w 为只写;rw 为读写。 
为了保证可以进行随机读取,所有写入的名字都是8个字节,写入的数字都是固定的4个字节。
 
import java.io.File;
import java.io.RandomAccessFile;
 
public class RandomAccessFileDemo01 {
      // 所有的异常直接抛出,程序中不再进行处理
      public static void main(String args[]) throws Exception {
           File f = new File("d:" + File.separator + "test.txt"); // 指定要操作的文件
           RandomAccessFile rdf = null// 声明RandomAccessFile类的对象
           rdf = new RandomAccessFile(f, "rw");// 读写模式,如果文件不存在,会自动创建
           String name = null;
           int age = 0;
           name = "zhangsan"; // 字符串长度为8
           age = 30; // 数字的长度为4
           rdf.writeBytes(name); // 将姓名写入文件之中
           rdf.writeInt(age); // 将年龄写入文件之中
           name = "lisi    "; // 字符串长度为8
           age = 31; // 数字的长度为4
           rdf.writeBytes(name); // 将姓名写入文件之中
           rdf.writeInt(age); // 将年龄写入文件之中
           name = "wangwu  "; // 字符串长度为8
           age = 32; // 数字的长度为4
           rdf.writeBytes(name); // 将姓名写入文件之中
           rdf.writeInt(age); // 将年龄写入文件之中
           rdf.close(); // 关闭
      }
};
 

(2)读取数据

读取是直接使用 r 的模式即可,以只读的方式打开文件。
读取时所有的字符串只能按照byte数组方式读取出来,而且长度必须和写入时的固定大小相匹配。 
 
import java.io.File;
import java.io.RandomAccessFile;

public class RandomAccessFileDemo02{
    // 所有的异常直接抛出,程序中不再进行处理
    public static void main(String args[]) throws Exception{
        File f = new File("d:" + File.separator + "test.txt") ;    // 指定要操作的文件
        RandomAccessFile rdf = null ;        // 声明RandomAccessFile类的对象
        rdf = new RandomAccessFile(f,"r") ;// 以只读的方式打开文件
        String name = null ;
        int age = 0 ;
        byte b[] = new byte[8] ;    // 开辟byte数组
        
// 读取第二个人的信息,意味着要空出第一个人的信息
        rdf.skipBytes(12) ;        // 跳过第一个人的信息
        for(int i=0;i<b.length;i++){
            b[i] = rdf.readByte() ;    // 读取一个字节
        }
        name = new String(b) ;    // 将读取出来的byte数组变为字符串
        age = rdf.readInt() ;    // 读取数字
        System.out.println("第二个人的信息 --> 姓名:" + name + ";年龄:" + age) ;
        // 读取第一个人的信息
        rdf.seek(0) ;    // 指针回到文件的开头
        for(int i=0;i<b.length;i++){
            b[i] = rdf.readByte() ;    // 读取一个字节
        }
        name = new String(b) ;    // 将读取出来的byte数组变为字符串
        age = rdf.readInt() ;    // 读取数字
        System.out.println("第一个人的信息 --> 姓名:" + name + ";年龄:" + age) ;
        rdf.skipBytes(12) ;    // 空出第二个人的信息
        for(int i=0;i<b.length;i++){
            b[i] = rdf.readByte() ;    // 读取一个字节
        }
        name = new String(b) ;    // 将读取出来的byte数组变为字符串
        age = rdf.readInt() ;    // 读取数字
        System.out.println("第三个人的信息 --> 姓名:" + name + ";年龄:" + age) ;
        rdf.close() ;                // 关闭
    }
};
 

运行结果

第二个人的信息 --> 姓名:lisi    ;年龄:31
第一个人的信息 --> 姓名:zhangsan;年龄:30
第三个人的信息 --> 姓名:wangwu  ;年龄:32
初步了解了RandomAccessFile类,下面我们演示下如何合并文件,代码如下:
    	  File file = new File("e:\\file.txt");  //文件内容为:今天挣了
    	  File file1 = new File("e:\\file1.txt");  //文件内容为:1000元
    	 
    	  RandomAccessFile file2 = new RandomAccessFile("e:\\file2.txt","rw");
    	
    	  BufferedReader is = new BufferedReader(new FileReader(file));
    	  BufferedReader is1 = new BufferedReader(new FileReader(file1));
    	  
    	  String line = "";
    	  
    	  while((line = is.readLine())!=null){
    		  file2.write(line.getBytes());
    	  }
    	  
    	  file2.seek(file2.getFilePointer());
    	  while((line = is1.readLine())!=null){
    		  file2.write(line.getBytes());
    	  }
    	  
    	  /**
    	   * 输出file2的内容
    	   */
    	  RandomAccessFile file3 = new RandomAccessFile("e:\\file2.txt","r");
    	  byte[] b = new byte[(int)file3.length()];
    	  byte[] c = new byte[(int)file3.length()];
    	  
    	  for(int i=0;i<b.length;i++){
    		  c[i] = file3.readByte();
    	  }
    	  
    	  String aa = new String(c);
    	  System.out.println(aa);
    	 
    	  file2.close();
    输出结果为:今天挣了1000元。
 
 第四步:验证Flash身份
       因为上传服务要提供web服务,所以需要每次验证下Flash的身份,这里我们采用MD5验签的方法,如果是java间也可以采用公钥私钥的方法。
      这里采用的方式是双方规定一种加密的格式,然后把Flash端Md5加密的结果和服务器端Md5加密的结果进行比较,如果相同则认为验证成功。
      代码如下:
     
		String clientSignMd5 = request.getParameter("sign");
		
		MD5 md5 = new MD5();		
		Date date = new Date();

		SimpleDateFormat sdf  = new SimpleDateFormat("yyyyMMdd");
		String serversign = AppType.SPACE + "-xxxxxxxxx-" + sdf.format(date);
		String serverSignMd5 = md5.encodeByMD5(serversign);
		
		if(!clientSignMd5.equals(serverSignMd5)){
			return ReturnMessageUtil.createErrorMsg("没有上传权限");
		}
 
第五步:ActiveMQ 发送转码请求。这里参考另一篇博客《ActiveMq实例》。
第六步:转码并截图。这里参考另一篇博客《Ffmpeg实例》。
第七步:合成图片。因为FLASH端放视频的时候,需要有视频图片的概览,就是每几分钟显示一张缩略图,但是如果每张图都要从FLASH端去读取的话,那么对前端的压力会很大,故把所有的缩略图合成一张大图,这样FLASH只需要请求一次这个大图就成了,然后FLASH端自己根据给定的尺寸切图显示。
至此就完成了一整套上传视频-转码抽图的功能了。
 
 
 
分享到:
评论

相关推荐

    基于go的大文件切片上传、断点续传、秒传.zip

    基于go的大文件切片上传、断点续传、秒传.zip 1、如何唯一标示一个文件? 文件的信息后端会存储在mysql数据库表中。 在上传之前,前端通过 spark-md5.js 计算文件的md5值以此去唯一的标示一个文件。 spark-md5.js ...

    FTP上传(断点续传)更新进度条

    在实际开发中,开发者可能会选择使用第三方库如libcurl、ftpclient等,它们已经封装了FTP协议和断点续传的功能,简化了开发过程。同时,开发者需要考虑错误处理,比如网络中断、服务器无响应等情况,确保在异常发生...

    oss下载(断点续传,后台显示进度)源代码.rar

    在开发过程中,特别是涉及大文件下载时,断点续传功能尤为重要,因为它允许用户在中断下载后从上次停止的地方继续,而无需重新开始。此外,后台显示下载进度的功能则可以提升用户体验,让用户清晰地了解文件下载的...

    Android多线程断点续传下载网络上的音/视频等各种文件

    在Android开发中,实现多线程断点续传下载网络上的音视频文件是一项重要的技能,尤其对于提升用户体验和节省用户流量至关重要。断点续传允许用户在暂停或因网络问题中断下载后,从上次停止的位置继续,而多线程则能...

    springboot+vue 大文件上传 包括断点续传 秒传 分片上传.zip

    本项目"springboot+vue 大文件上传 包括断点续传 秒传 分片上传.zip"提供了一套完整的解决方案,针对大文件上传进行了优化,确保了上传的高效性和可靠性。 首先,我们来看SpringBoot的部分。SpringBoot是基于Spring...

    多线程断点续传下载实现

    - **OkHttp**:广泛使用的网络请求库,支持设置请求头中的Range字段,实现断点续传。 - **Volley**:虽然Volley不直接支持断点续传,但可以通过自定义Request实现。 - **Retrofit**:配合OkHttp,可以方便地构建...

    Android大文件断点续传

    在Android开发中,大文件的断点续传是一项重要的技术,尤其在处理视频、音频或者大型数据文件时显得尤为重要。断点续传允许用户在上传或下载过程中因网络中断而中断后,从上次中断的位置继续,而不是从头开始,极大...

    android在线播放断点续传

    在Android平台上,开发一款音乐播放器并实现在线播放和断点续传功能是一项常见的任务。这一功能能够提高用户体验,特别是对于大文件或者网络环境不稳定的用户来说,它允许用户在中断后从上次停止的地方继续播放,而...

    多线程+断点续传 完整资源!

    为了达到这一目标,多线程和断点续传技术成为了文件下载领域中的关键技术。本篇文章将详细介绍这两个概念,并探讨它们如何共同工作以提供更优的下载体验。 多线程技术源自于操作系统级的概念,它允许程序同时执行多...

    jquery-大文件上传插件,支持分片上传,断点续传

    "jquery-大文件上传插件,支持分片上传,断点续传"就是这样一个解决方案,它针对大文件上传进行了优化,确保了高效且用户友好的体验。 这个插件的核心特性包括: 1. **分片上传**:由于大文件一次性上传可能会导致...

    springMVC框架下plUpload断点续传

    在Spring MVC框架中,plUpload是一个非常实用的前端文件上传插件,尤其适用于处理大文件,如视频和图片的断点续传。该插件支持多浏览器兼容,提供了丰富的功能和自定义选项,使得文件上传体验更加友好。下面将详细...

    AFNetWorking的常用封装上传下载断点续传等

    在这个主题中,我们将深入探讨AFNetworking的常用封装,包括GET、POST请求以及上传下载功能,特别是断点续传技术。 首先,让我们从基础开始。AFNetworking的核心在于`AFHTTPSessionManager`类,它是对...

    IOS 多线程异步下载,断点续传

    ASIHTTPRequest是一个流行的iOS网络库,它提供了异步HTTP请求的功能,包括断点续传和进度回调。尽管现在已被更现代的`NSURLSession`替代,但在一些老项目中仍可能看到它的身影。使用ASIHTTPRequest进行断点续传下载...

    VC++ FTP、HTTP 多线程断点续传下载文件源码.rar

    4. **断点续传**:断点续传功能允许用户在下载过程中暂停,然后在稍后的时间从停止的地方继续下载,而不是从头开始。这对于网络不稳定或者需要在不同时间下载的情况非常有用。实现这一功能需要服务器支持,并且...

    Android多线程断点续传下载+在线播放音乐.rar

    在Android应用开发中,实现多线程断点续传下载和在线播放音乐是两个重要的功能。下面我们将详细探讨这两个主题。 一、Android多线程断点续传下载 1. 多线程下载:传统的单线程下载方式速度受限于网络环境,而多...

    多线程socket文件传输_支持断点续传_收发消息_点对多点

    本文将深入探讨一种基于多线程Socket技术实现的文件传输方案,该方案支持断点续传、收发消息以及点对多点通信。我们将讨论如何使用VC++(Visual C++)环境来开发这样的系统,以及涉及的关键技术和概念。 首先,让...

    HTTP进度下载断点续传.rar

    要实现断点续传,客户端需要在下载开始前向服务器发送一个`Range`请求头,指定希望从哪个字节位置开始下载。例如,如果之前下载到第50000字节,那么请求头可能会是`Range: bytes=50001-`。服务器收到这个请求后,将...

    Android之多线程下载及断点续传

    在Android开发中,多线程下载和断点续传是两个关键的概念,它们极大地提高了应用程序在处理大文件,如音频、视频或应用安装包时的性能和用户体验。下面将详细介绍这两个概念及其应用。 **一、多线程下载** 1. **多...

    断点续传demo,亲测可用,完整的demo

    - `pom.xml`:这是Maven项目的配置文件,其中包含了项目依赖、构建设置等信息,可能列出了用于实现断点续传的库,如Apache HttpClient等。 - `src`:源代码目录,包含实现断点续传逻辑的Java或其他编程语言的...

    Android功能源码《断点续传和网络mp3播放》

    在Android开发中,实现断点续传和网络MP3播放是两个重要的功能,它们涉及到网络通信、文件操作以及多媒体处理等多个技术领域。断点续传技术使得用户可以在网络不稳定或者中断后,继续从上次停止的地方下载文件,而...

Global site tag (gtag.js) - Google Analytics