`
dreamoftch
  • 浏览: 483029 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

断点文件上传流程

阅读更多

 

服务器和客户端交互过程中用到的属性:

文件名:        fileName

文件大小:        fileSize

用户IP:IP

文件断点位置:uploadSize

 

步骤1:用户选择目标文件,点击确定上传

 

步骤2:

通过注册表调用客户端,并且传递给客户端参数:参数格式为:uploadhelper://fileName=aaa.txt&fileName=bbb.txt¶m=p1¶m=p2

该参数作为main方法的参数args[0]传递进来

 

步骤3:

客户端将参数解析之后,解析出文件名列表(数组)和参数列表(map),然后将文件名列表发送给服务器,查询这些文件的断点记录,服务器根据这些文件名列表查找uploadConfig文件夹下相应文件的记录,并返回给客户端,服务器->客户端 响应格式:"fileName="+fileName+"&fileSize="+fileSize+"&uploadSize="+uploadSize多个文件之间用换行连接

 

步骤4:

客户端上传文件和参数,则客户端的发送格式为:

(如果有参数:)    第一行: --parameters--

       第二行: key1=value1&key2=value2........

(每个文件上传的格式:)多文件时循环执行

第一行: --file begin--

第二行:"fileName="+fileName+"&fileSize="+fileSize+"&uploadSize="+uploadSize

从第三行开始,从文件的断点处(RandomAccessFile.seek(uploadSize)),开始上传文件内容

 

步骤5:

服务器端读取第一行内容,

1:如果是--parameters--则说明该次上传存在参数,然后读取第二行,从第二行内容中解析出参数列表;然后读取文件内容(读取方式参照2)

2:如果是--file begin--则说明该次上传不存在参数,只有文件,然后进行文件读取;

文件读取步骤:当读取到--file begin--之后,在读取下一行内容:

"fileName="+fileName+"&fileSize="+fileSize+"&uploadSize="+uploadSize

从中解析出该文件从什么地方开始保存(uploadSize),文件名字(fileName)和文件大小(fileSize),

然后利用RandomAccessFile.setLength(fileSize),RandomAccessFile.seek(uploadSize),设定文件的大小,以及将文件操作的指针指向断点位置。

然后开始读取流的内容并保存到文件中,每保存blockSize,就更新uploadConfig文件夹下的配置文件,将该文件的断点位置(uploadSize)增加blockSize,这样就实现了断点的保存,下次上传就会从新的断点处开始上传,也就是断点上传的功能当一共读取了(uploadSize - fileSize)的文件之后,就完成了一个文件的上传,接下来再读取一行判断是否是--file begin--,如果是,则继续前面过程,否则说明上传结束,返回。

 

 

另外:

上传速度和百分比的功能:

在上面服务器保存文件过程中,同时启动定时器(时间间隔为500ms),定时器通过计算500ms时间内,服务器读取了多少文件,计算出上传速度(rate),根据当前断点位置和文件大小,计算出文件上传百分比(percent),并将rate和percent保存到uploadRate文件夹下的配置文件中,页面使用ajax将文件名传给服务器,服务器查询该文件的rate和percent并返回给页面,实现页面显示速度和百分比的功能

 

注册表注册功能:这里使用到了registry.jar

当用户双击exe文件时,程序的main方法接收到的args参数为空,此时默认为进行注册表注册;

然后使用registry.jar提供的api往注册表写入内容,进行注册

 

代码:

import java.io.File;

import com.ice.jni.registry.NoSuchKeyException;
import com.ice.jni.registry.RegStringValue;
import com.ice.jni.registry.Registry;
import com.ice.jni.registry.RegistryException;
import com.ice.jni.registry.RegistryKey;

public class RegistryUtils {	/**
	 * 
	 *  Created on 2013-5-8 
	 * <p>Discription:判断注册表是否已经注册</p>
	 */
	public static boolean isRegistered(){
		File file = new File("");
		String path = file.getAbsolutePath()+File.separator+"upload.exe";
		try {
			 RegistryKey root = Registry.HKEY_CLASSES_ROOT;
	         RegistryKey uploadhelper = root.openSubKey("uploadhelper");
	         String protocol = uploadhelper.getStringValue("URL Protocol");
	         if(protocol != null && protocol.equals(path)){
	        	 return true;
	         }else if(protocol != null && !protocol.equals(path)){
	        	 deleteRegistry("uploadhelper");
	        	 return false;
	         }
	       } catch (NoSuchKeyException e) {
	    	   System.out.println("没有这项注册表");
	    	   return false;
	       } catch (RegistryException e) {
	    	   return false;
	       }   
	    file = null;
	    return false;
	}
	
	public static void deleteRegistry(String subKey){
		try {
			RegistryKey root = Registry.HKEY_CLASSES_ROOT;
			root.deleteSubKey(subKey);
			root.closeKey();
		} catch (NoSuchKeyException e) {
			e.printStackTrace();
		} catch (RegistryException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 *  Created on 2013-5-8 
	 * <p>Discription:为程序注册注册表</p>
	 */
	public static void register(){
		 try {
			 File file = new File("");
			 String path = file.getAbsolutePath()+File.separator+"upload.exe";
	         RegistryKey root = Registry.HKEY_CLASSES_ROOT;
	         RegistryKey uploadhelper = root.createSubKey("uploadhelper", "");
	         uploadhelper.setValue(new RegStringValue(uploadhelper, "URL Protocol",path));
	         RegistryKey shell = uploadhelper.createSubKey("shell", "");
	         RegistryKey open = shell.createSubKey("open", "");
	         RegistryKey command = open.createSubKey("command", "");
	         command.setValue(new RegStringValue(command,"","\"" + path + "\" \"%1\""));
	       } catch (NoSuchKeyException e) {
	         e.printStackTrace();
	       } catch (RegistryException e) {
	         e.printStackTrace();
	       }
	}}

 

 

客户端代码:

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;

import com.lubansoft.test.upload.po.UploadRecord;
import com.lubansoft.test.upload.utils.ConfigFileUtils;

public class SimpleUrlConnection {
	/**
	 * main方法的参数格式约定为(无论是文件还是参数,中间均使用 “&” 连接):
	 *  uploadhelper://fileName=aaa.txt&fileName=bbb.txt&param=p1&param=p2
	 */
	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		/**
		 * 如果参数为 null ,则默认为进行注册表注册
		 */
		if(args != null && args.length > 0){
			String arguments = args[0];
			arguments = arguments.substring("uploadhelper://".length(), arguments.length());
			args[0] = arguments;
			SimpleUrlConnection suc =  new SimpleUrlConnection();
			Map<String,Object> params = ConfigFileUtils.convertArgument(arguments);
			suc.fileNames = ((List<String>)params.get("fileName")).toArray(new String[0]);
			suc.params = ((Map<String,String>)params.get("param"));
			suc.executeuUpload();
		}else{
			if(!RegistryUtils.isRegistered()){
				RegistryUtils.register();
			}
		}
	}
	private HttpURLConnection conn;
	boolean isShowRate = false;
	int streamLength = 10240;
	private String[] fileNames;
	private Map<String,String> params;
	
	private UploadRecord[] uploadRecords;
	
	public SimpleUrlConnection(){
	}
	public SimpleUrlConnection(String[] pathNames){
		this.fileNames = pathNames;
	}
	
	//上传文件
	private void executeuUpload() {
		/**
		 * 上传文件之前先获取所有文件上传记录
		 */
		uploadRecords  = getUploadRecord(fileNames);
		/**
		 * 上传文件
		 */
		upload(params,uploadRecords, fileNames);
	}
	
	/**
	 *  Created on 2013-5-13 
	 * <p>Discription:查询文件上传记录</p>
	 */
	public UploadRecord[] getUploadRecord(String[] fileNames){
		UploadRecord[] records = null;
		if(fileNames == null || fileNames.length<=0){
			return null;
		}
		records = new UploadRecord[fileNames.length];
		OutputStream out = null;
		InputStream in = null;
		try {
			URL url = new URL("http://localhost:8080/upload/getUploadHistory");
			conn = (HttpURLConnection) url.openConnection();
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setUseCaches(false);
			conn.setRequestMethod("POST");
			conn.setRequestProperty("connection", "Keep-Alive");
			conn.setRequestProperty("Charsert", "UTF-8");
			out = conn.getOutputStream();
			File f = null;
			StringBuilder sb = new StringBuilder();
			int length = fileNames.length;
			for(int i=0;i<length;i++){
				f = new File(fileNames[i]);
				sb.append("fileName="+f.getName()+"&fileSize="+f.length());
				if(i == length-1){
					sb.append("\n");
				}else{
					sb.append(";");
				}
			}
			String request = sb.substring(0, sb.length()-1);
			out.write(request.getBytes("utf-8"));
			out.flush();
			
			in = conn.getInputStream();
			String line = null;
			for(int i=0;i<fileNames.length;i++){
				line = ConfigFileUtils.readLine(in);
				records[i] = getUploadRecoreFromString(line);
			}
			isShowRate = true;
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			try {
				in.close();
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return records;
	}
	
	/**
	 *  Created on 2013-5-13 
	 * <p>Discription:从字符串中提取出UploadRecord对象 :</p>
	 * <p>"fileName="+f.getName()+"&fileSize="+f.length()+"&uploadSize="f.getUploadSize()</p>
	 */
	private UploadRecord getUploadRecoreFromString(String line) {
		return ConfigFileUtils.getUploadRecoreFromString(line);
	}

	/**
	 *  Created on 2013-5-13 
	 * <p>Discription:根据服务器返回的UploadRecord[] uploadRecords,开始从断点处上传</p>
	 */
	public void upload(Map<String,String> params,UploadRecord[] uploadRecords,String[] paths){
		try {
			String url = "http://localhost:8080/upload/uploadServlet";
			URL u = new URL(url);
			HttpURLConnection conn = (HttpURLConnection) u.openConnection();
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setUseCaches(false);
			conn.setRequestMethod("POST");
			conn.setRequestProperty("connection", "Keep-Alive");
			conn.setRequestProperty("Charsert", "UTF-8");
			//指定流的大小,当内容达到这个值的时候就把流输出
			conn.setChunkedStreamingMode(1024*1024);
			OutputStream out = conn.getOutputStream();
			if(params != null){
				/**
				 * (如果有参数:)    第一行: --parameters--
 			     *                         第二行: key1=value1&key2=value2........
				 */
				StringBuilder sb = new StringBuilder();
				for(Map.Entry<String, String> entry:params.entrySet()){
					sb.append(entry.getKey() + "=" + entry.getValue() + "&");
				}
				String param = sb.substring(0, sb.length()-1);
				/**
				 * 首先告诉服务器要传输的是参数
				 */
				out.write("--parameters--\n".getBytes("utf-8"));
				out.write(param.getBytes("utf-8"));
				out.write("\n".getBytes("utf-8"));
			}
			if(uploadRecords != null && uploadRecords.length>0){
				/**
				 * 接下来告诉服务器传递的是文件
				 */
				RandomAccessFile in = null;
				File file = null;
				UploadRecord uploadRecord = null;
				/**
				 * 通过for循环来上传多个文件,每个文件的上传过程为:
				 * 第一行:--file begin--
				 * 第二行:fileName=ddd&fileSize=xxx&uploadSize=aaa
				 * 第三行:开始上传文件内容
				 */
				for(int i=0;i<uploadRecords.length;i++){
					uploadRecord = uploadRecords[i];
					if(uploadRecord.getFileSize() <= uploadRecord.getUploadSize()){
						continue;
					}
					out.write("--file begin--\n".getBytes("utf-8"));
					out.write(("fileName="+uploadRecord.getFileName()+"&fileSize="+uploadRecord.getFileSize()+"&uploadSize="+uploadRecord.getUploadSize()).getBytes("utf-8"));
					out.write("\n".getBytes("utf-8"));
					file = new File(paths[i]);
					in = new RandomAccessFile(file, "r");
					byte[] b = null;
					int n = -1;
					/**
					 * 将指针移动到断点位置
					 */
					in.seek(uploadRecords[i].getUploadSize());
					b = new byte[2048];
					/**
					 * 上传文件内容
					 */
					while ((n = in.read(b)) != -1) {
						out.write(b, 0, n);
					}
					out.flush();
				}
			}
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					conn.getInputStream()));
			String line = null;
			while ((line = reader.readLine()) != null) {
				System.out.println(line);
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

 

 

测试页面代码:

<%@ 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>
    <base href="<%=basePath%>">
    
    <title>My JSP 'index.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<style type="text/css">
		#confirm{
			visibility:hidden;
		}
		#rate_div{
			display:none;
		}
	</style>
	<script type="text/javascript" src="upload/js/jquery.min.js"></script>
	<script type="text/javascript">
		function upload(file){
			//格式为:uploadhelper://fileName=aaa.txt&fileName=bbb.txt&param=p1&param=p2
			$("#upload_link").attr("href","");
		    fileNames = null;
			$("input[type='file']").each(function(i){
				var e = $(this);
				if(e.val()){
					//为fileNames增加元素
					if(fileNames){
						fileNames[fileNames.length] = e.val();
					}else{
						fileNames = [e.val()];
						fileName = e.val();
					}
					//为超链接修改href属性
					if($("#upload_link").attr("href")){
						$("#upload_link").attr("href",$("#upload_link").attr("href")+"&fileName="+ e.val());
					}else{
						$("#upload_link").attr("href","uploadhelper://fileName=" +  e.val());
					}
				}
			});
			$("#confirm").css('visibility','visible');
		}
		
		//定义一个定时器去显示下载速度和百分比
		var getUploadRateTimeout ;
		//多文件上传的时候,所有文件的文件路径
		var fileNames;
		//当前上传的文件
		var fileName;
		var uploadFileIndex = 0;
		
		function confirmUpload(){
			$("#rate_div").css('display','block');
			if(getUploadRateTimeout){
				clearTimeout(getUploadRateTimeout);
			}
			getUploadRateTimeout = setTimeout("getUploadRate()",500);
		}
		function getUploadRate(){
			var url = '${pageContext.request.contextPath}/servlet/getRateAndPercent';
			var param={filepath:fileName};
			$.post(url,param,function(data){
				showRate(data);
			});
		}
		function showRate(data){
			var result = eval("("+data+")");
			$("#fileName_span").html(result.fileName);
			$("#rate_span").html(result.rate);
			$("#percent_span").html(result.percent);
			getUploadRateTimeout = setTimeout("getUploadRate()",500);
			if(result.percent == 100){
				uploadFileIndex ++;
				if(uploadFileIndex >= fileNames.length){
					alert('上传已完成!');
					clearTimeout(getUploadRateTimeout);
					getUploadRateTimeout = null;
				}else{
					fileName = fileNames[uploadFileIndex];
				}
			}
		}
		function uploadTest(){
			if($("#upload_link").attr("href")){
				window.location=$("#upload_link").attr("href");
			}
		}
		function test(){
			var url = '${pageContext.request.contextPath}/servlet/getRateAndPercent';
			var param="E:\\资料\\资料.rar";
			$.post(url,{filepath:param},function(data){
				showRate(data);
			});
		}
	</script>

  </head>
  
  <body>
   	选择上传文件:<input type="file" onchange="upload(this)" id="fileupload1"/><br/>
   	选择上传文件:<input type="file" onchange="upload(this)" id="fileupload2"/><br/>
   	选择上传文件:<input type="file" onchange="upload(this)" id="fileupload3"/><br/>
  		<div id="confirm"> <a id="upload_link" onclick="confirmUpload()">确定上传</a></div>
  		<div id="rate_div">
  			当前上传文件:<span id="fileName_span"></span><br/>
  			上传速度:<span id="rate_span"></span> kb/s  <br/>
  			文件上传百分比:<span id="percent_span"></span> %  <br/>
  		</div>
  	<input type="button" onclick="uploadTest()" value="测试"/>
  </body>
</html>

 

  • src.rar (22.4 KB)
  • 下载次数: 21
分享到:
评论
2 楼 dreamoftch 2015-07-20  
dannielhome 写道
请问下这个upload.exe是什么东西呢


是自己做的一个本地应用程序,上传的时候需要用的
1 楼 dannielhome 2015-07-20  
请问下这个upload.exe是什么东西呢

相关推荐

    断点上传示例源码2012825

    断点上传示例源码 源码描述: 此项目分为客户端和服务器端两部分 总体流程为: 1.客户端在上传文件前,先获取服务器端文件的大小(通过访问服务器端页面获取),即偏移指针 2.客户端从偏移指针处开始读取后续的文件...

    winform断点上传

    此项目分为客户端和服务器端两部分总体流程为:1.客户端在上传文件前,先获取...客户端从偏移指针处开始读取后续的文件流,将文件流上传到服务器3.服务器获取到传输的文件流后,从偏移指针处开始续写服务器端的文件流

    iOS实现文件切片储存并且上传(仿断点续传机制)

    我们在开发中,一般在视频类的app或者与硬件交互的app中会有将数据文件上传到云端,少数社交app上传图片也比较多。下面讲的是将数据文件(txt类型)切片储存到本地并逐片上传到云端模仿断点续传的机制,但事实上,这...

    C#.net 断点续传源程序

    2.客户端从偏移指针处开始读取后续的文件流,将文件流上传到服务器 3.服务器获取到传输的文件流后,从偏移指针处开始续写服务器端的文件流,直到传输完成 服务器端可以采用 aspx 、WebService、Servlet 等各种方式...

    C# socket TCP 大文件传输同时实现断点续传

    本程序是自己为公司服务器备份文件迁移写的一个基于socket TCP协议的大文件传输应用程序。代码实现了基本流程,后期还有更改和优化。先把前期demo上传以流自己以后学习。如大家有需要可以下载一起探讨。

    ng-quick-upload:angular 的大文件上传器

    NgQuickStart ** NgQuickStart 是一个angulra2+的文件秒传+队列上传+切片...大文件上传有很多思路,前后端文件上传也有几种方法,在这里我就不一一列举。 一. 队列上传 大多数情况下,尤其是在PC端因为流量相对较大 所

    桃源文件系统v3.3

    3、多模式文件上传:支持多达5种文件上传模式,包括计算机浏览器普通上传、手机WAP浏览器普通上传(需安装WAP服务组件)、浏览器ActiveX插件批量文件上传、添加文件网址直接下载文件到用户空间、访客持上传码上传...

    React+EggJs实现断点续传的示例代码

    该功能实现流程是先把上传的文件进行切割,然后把切割之后的文件块发送到服务端,发送完毕之后通知服务端组合文件块。 其中暂停上传功能就是前端取消掉文件块的上传请求,恢复上传则是把未上传的文件块重新上传。...

    桃源企业文件管理系统v3.2

    3、多模式文件上传:支持多达5种文件上传模式,包括计算机浏览器普通上传、手机WAP浏览器普通上传(需安装WAP服务组件)、浏览器ActiveX插件批量文件上传、添加文件网址直接下载文件到用户空间、访客持上传码上传...

    桃源企业文件管理系统 3.2.rar

    3、多模式文件上传:支持多达5种文件上传模式,包括计算机浏览器普通上传、手机WAP浏览器普通上传(需安装WAP服务组件)、浏览器ActiveX插件批量文件上传、添加文件网址直接下载文件到用户空间、访客持上传码上传...

    Large-file-uplaod:大文件分片上传 (Large files are uploaded in slices)

    大文件上传项目地址界面截图:大文件分片整体流程计算文件摘要id文件切片并上传断点续传上传进度与暂停分片上传完成-后端进行分片合并流程图前端内容计算文件摘要id使用spark-md5 计算文件idconst spark = new ...

    仅用于Android上开发关于文件上传、下载功能的开发工具包,采用了OkHttp作为http请求处理。.zip

    包括编译器、构建工具(如Make、Gradle、Maven)等,用于将源代码转换为可执行文件或库,并进行资源打包、优化等处理。 调试与测试: 集成调试器允许开发者逐行执行代码,设置断点、查看变量值、跟踪调用堆栈等...

    课程实验基于Java实现的分布式存储系统源码+项目说明.tar

    3. 用户根据 chunk 信息,可以向 Client 上传文件分片,Client 转发给 chunk-server 完成分片文件上传。 4. 从用户端上传到 Client 时需要做md5校验以确保文件完整性。chunk-server 上传完成后,同样下发一个 md5 ...

    Fly.Box 企业网盘 v2.2.1

    随着信息技术的快速发展,给我们日常生活、办公方式和业务流程带来了巨大改变,大量数据信息被创建,各种各样的数据文件散布在每个人的多种设备、不同的网络环境中,如何有效的管理这些...2、上传2.1断点续传、更可靠

    浅谈关于JS下大批量异步任务按顺序执行解决方案一点思考

    最近需要做一个浏览器的, 支持大体积文件上传且要支持断点续传的上传组件, 本来以为很容易的事情, 结果碰到了一个有意思的问题: 循环执行连续的异步任务, 且后一个任务需要等待前一个任务的执行状态 这么说可能...

    协通XT800远程协助软件个人版 v1.0.0

    协通XT800是一款功能强大操作简单,可穿透防火墙的远程...3、文件传输:可传输大容量文件,支持断点续传,可直接发送的文件夹。三、轻便的即时通讯服务:1、内置轻便的即时通讯服务,将帮助您与同事或客户进行快捷、

    JAVA上百实例源码以及开源项目

    6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...

    java源码包---java 源码 大量 实例

    6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...

    java源码包2

    6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...

Global site tag (gtag.js) - Google Analytics