服务器和客户端交互过程中用到的属性:
文件名: 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¶m=p1¶m=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¶m=p1¶m=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>
相关推荐
断点续传是一种高效、可靠的文件上传技术,尤其在处理大文件时,它能显著提升用户体验。这种技术允许用户在上传过程中暂停、恢复上传,而不会丢失已经上传的数据。在现代互联网应用中,如云存储服务、在线协作平台等...
"jquery-大文件上传插件,支持分片上传,断点续传"就是这样一个解决方案,它针对大文件上传进行了优化,确保了高效且用户友好的体验。 这个插件的核心特性包括: 1. **分片上传**:由于大文件一次性上传可能会导致...
3. 断点续传支持:服务器需要记录每个文件上传的状态,包括已接收的块和每个块的字节范围。当客户端重新连接并请求续传时,服务器返回当前状态,客户端根据此信息决定从哪个块开始上传。 4. 文件合并:所有块都成功...
总之,实现Struts2的断点上传需要对文件上传流程进行深入理解和定制,涉及文件分块、服务器存储、客户端状态管理等多个方面。通过这样的定制,我们可以提供更优的用户体验,尤其是在处理大文件和网络不稳定的情况下...
在IT领域,断点上传是一种高效且用户友好的文件上传技术,尤其对于大文件而言,它允许用户在中断网络连接后从上次停止的地方继续上传,而无需重新开始整个过程。这个"多文件断点上传实例"很可能是某种编程示例或教程...
综上所述,这个主题涵盖了从用户交互的文件上传和下载功能,到服务器端的断点续传实现,再到使用AOP进行操作日志记录的整个流程。理解并掌握这些知识点对于开发高效、健壮的Web应用程序至关重要。
ASP.NET提供了丰富的HttpModule和HttpHandler接口,可以用来定制文件上传的处理流程。 6. **安全性考虑**:在实现这些高级上传功能时,必须注意安全问题。例如,防止文件覆盖、限制上传文件类型和大小以防止DoS攻击...
"Silverlight + WebService 文件断点上传"是一种有效解决大文件上传问题的技术,允许用户在上传过程中暂停并继续,避免了网络中断或资源限制导致的上传失败。本文将深入探讨这一技术的实现原理及关键步骤。 【断点...
断点上传示例源码 源码描述: 此项目分为客户端和服务器端两部分 总体流程为: 1.客户端在上传文件前,先获取服务器端文件的大小(通过访问服务器端页面获取),即偏移指针 2.客户端从偏移指针处开始读取后续的文件...
在处理大文件上传时,Silverlight提供了高级功能,如支持断点续传,这在用户上传大文件时尤其有用,因为它们可以暂停、恢复上传,而无需重新开始整个过程。以下是关于Silverlight大文件上传支持断点续传的详细知识点...
在本文中,我们将深入探讨如何使用C#编程语言来实现FTP(文件传输协议)上传功能,特别是支持断点续传的特性。FTP是一种标准网络协议,用于在客户端和服务器之间交换文件。C#中的System.Net命名空间提供了丰富的类库...
以下是一个简单的AJAX大文件上传流程: 1. **前端准备**:在用户选择文件后,使用FileReader API读取文件并切割成多个块。每个块通常设定为几MB大小,以平衡上传速度和服务器处理能力。 2. **分块上传**:对每个...
在现代Web应用中,大文件上传和断点续传功能是提高用户体验的关键因素。这个解决方案主要涉及前端技术,包括Vue框架、Element-UI组件库,以及利用Blob对象、FileReader API、WebWorker和spark-md5库来实现。下面将...
ntko大文件上传控件是专门用于处理大文件上传的软件组件,对于Web应用程序的开发人员来说,它提供了一种高效、稳定且用户友好的解决方案。这个控件能够有效地解决传统HTTP上传方式在处理大文件时可能出现的问题,如...
WebUploader是一款优秀的JavaScript插件,专门用于解决大文件上传的问题,它支持进度条显示以及断点续传功能,确保用户可以高效且稳定地上传大容量文件。以下是对这些知识点的详细说明: 1. **大文件上传**: 大...
"jsp+vue 大文件上传 包括断点续传 秒传 分片上传" 是一个专为解决此类问题而设计的解决方案,它结合了Java服务器端技术(JSP)和前端框架Vue.js,提供了一整套完善的文件上传机制,包括断点续传、秒传和分片上传...
9. **性能优化**:对于大文件上传,可能需要考虑分块上传、断点续传等功能,以提高用户体验并减少服务器压力。 10. **进度反馈与用户界面**:在客户端,可以使用JavaScript或AJAX提供上传进度反馈,更新用户界面,...
首先,我们需要理解的是Android文件上传的基本流程。通常,小文件的上传可以通过HttpURLConnection或者OkHttp等HTTP库来完成,但这些方法并不适合大文件,因为它们无法很好地处理网络中断的情况。因此,我们通常会...