java两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下:
实现思路:
1、服:利用ServerSocket搭建服务器,开启相应端口,进行长连接操作
2、服:使用ServerSocket.accept()方法进行阻塞,接收客户端请求
3、服:每接收到一个Socket就建立一个新的线程来处理它
4、客:利用Socket进行远程连接,询问已上传进度
5、客:使用FileInputStream.skip(long length)从指定位置读取文件,向服务器发送文件流
6、服:接收客户端输入流,使用RandomAccessFile.seek(long length)随机读取,将游标移动到指定位置进行读写
7、客/服:一个循环输出,一个循环读取写入
8、示例:以下是具体代码,仅供参考
文件介绍:
FileUpLoadServer.java(服务器接收文件类)
FileUpLoadClient.java(客户端发送文件类)
FinalVariables.java(自定义参数类)
SocketServerListener.java(JavaWeb启动Socket操作类)
web.xml(配置文件,跟随项目启动)
断点上传(服务端)
packagecom.cn.csdn.seesun2012.socket;
importjava.io.DataInputStream;
importjava.io.DataOutputStream;
importjava.io.File;
importjava.io.FileOutputStream;
importjava.io.IOException;
importjava.io.RandomAccessFile;
importjava.math.RoundingMode;
importjava.net.ServerSocket;
importjava.net.Socket;
importjava.text.DecimalFormat;
publicclassFileUpLoadServerextendsServerSocket{
//文件大小
privatestaticDecimalFormat df=null;
//退出标识
privatebooleanquit=false;
static{
//设置数字格式,保留一位有效小数
df=newDecimalFormat("#0.0");
df.setRoundingMode(RoundingMode.HALF_UP);
df.setMinimumFractionDigits(1);
df.setMaximumFractionDigits(1);
}
publicFileUpLoadServer(intreport)throwsIOException{
super(report);
}
/**
*使用线程处理每个客户端传输的文件
*
* @throws Exception
*/
publicvoidload()throwsException{
System.out.println("【文件上传】服务器:"+this.getInetAddress()+"正在运行中...");
while(!quit){
// server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的
Socket socket=this.accept();
/**
*我们的服务端处理客户端的连接请求是同步进行的, 每次接收到来自客户端的连接请求后,
*都要先跟当前的客户端通信完之后才能再处理下一个连接请求。 这在并发比较多的情况下会严重影响程序的性能,
*为此,我们可以把它改为如下这种异步处理与客户端通信的方式
*/
//收到请求,验证合法性
String ip=socket.getInetAddress().toString();
ip=ip.substring(1,ip.length());
System.out.println("服务器接收到请求,正在开启验证对方合法性IP:"+ip+"!");
//每接收到一个Socket就建立一个新的线程来处理它
newThread(newTask(socket,ip)).start();
}
}
/**
*处理客户端传输过来的文件线程类
*/
classTaskimplementsRunnable{
privateSocket sk;//当前连接
privateString ips;//当前连接IP址
publicTask(Socket socket,String ip){
this.sk=socket;
this.ips=ip;
}
publicvoidrun(){
Socket socket=sk;//重新定义,请不要移出run()方法外部,否则连接两会被重置
String ip=ips;//重新定义,同上IP会变
longserverLength=-1l;//定义:存放在服务器里的文件长度,默认没有为-1
charpathChar=File.separatorChar;//获取:系统路径分隔符
String panFu="D:";//路径:存储文件盘符
DataInputStream dis=null;//获取:客户端输出流
DataOutputStream dos=null;//发送:向客户端输入流
FileOutputStream fos=null;//读取:服务器本地文件流
RandomAccessFile rantmpfile=null;//操作类:随机读取
try{
//获取
dis=newDataInputStream(socket.getInputStream());
//发送
dos=newDataOutputStream(socket.getOutputStream());
//定义客户端传过来的文件名
String fileName="";
while(fileName==""){
//读取客户端传来的数据
fileName=dis.readUTF();
System.out.println("服务器获取客户端文件名称:"+fileName);
File file=newFile(panFu+pathChar+"receive"+pathChar+""+ip+pathChar+fileName);
if(file.exists()){
serverLength=file.length();
dos.writeLong(serverLength);
System.out.println("向客户端返回文件长度:"+serverLength+" B");
}else{
serverLength=0l;
dos.writeLong(serverLength);
System.out.println("文件不存在");
System.out.println("向客户端返回文件长度:"+serverLength+" B");
}
}
System.out.println("服务器建立新线程处理客户端请求,对方IP:"+ip+",传输正在进行中...");
//从客户端获取输入流
dis=newDataInputStream(socket.getInputStream());
//文件名和长度
longfileLength=dis.readLong();
File directory=newFile(panFu+pathChar+"receive"+pathChar+""+ip+pathChar);
if(!directory.exists()){
directory.mkdirs();
}
intlength=0;
byte[]bytes=newbyte[1024];
File file=newFile(directory.getAbsolutePath()+pathChar+fileName);
if(!file.exists()){
//不存在
fos=newFileOutputStream(file);
//开始接收文件
while((length=dis.read(bytes,0,bytes.length))!=-1){
fos.write(bytes,0,length);
fos.flush();
}
}else{
//存储在服务器中的文件长度
longfileSize=file.length(),pointSize=0;
//判断是否已下载完成
if(fileLength>fileSize){
//断点下载
pointSize=fileSize;
}else{
//重新下载
file.delete();
file.createNewFile();
}
rantmpfile=newRandomAccessFile(file,"rw");
/*
* java.io.InputStream.skip()用法:跳过 n 个字节(丢弃) 如果 n
*为负,则不跳过任何字节。
*/
// dis.skip(pointSize);(已从客户端读取进度)
/**
*资源,文件定位(游标、指针) 将ras的指针设置到8,则读写ras是从第9个字节读写到
*/
rantmpfile.seek(pointSize);
while((length=dis.read(bytes,0,bytes.length))!=-1){
rantmpfile.write(bytes,0,length);
}
}
System.out.println("========文件接收成功 [File Name:"+fileName+"] [ClientIP:"+ip+"] [Size:"+getFormatFileSize(file.length())+"] ========");
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(fos!=null)
fos.close();
if(dis!=null)
dis.close();
if(rantmpfile!=null)
rantmpfile.close();
socket.close();
}catch(Exception e){
e.printStackTrace();
System.out.println("Socket关闭失败!");
}
/**
*文件传输完毕:执行后续操作(略)
*/
//DoSomeThing dst = new DoSomeThing()
//dst.save(filePath);
}
}
}
/**
*格式化文件大小
*
* @param length
* @return
*/
publicString getFormatFileSize(longlength){
doublesize=((double)length)/(1<<30);
if(size>=1){
returndf.format(size)+"GB";
}
size=((double)length)/(1<<20);
if(size>=1){
returndf.format(size)+"MB";
}
size=((double)length)/(1<<10);
if(size>=1){
returndf.format(size)+"KB";
}
returnlength+"B";
}
/**
*退出
*/
publicvoidquit(){
this.quit=true;
try{
this.close();
}catch(IOException e){
System.out.println("服务器关闭发生异常,原因未知");
}
}
}
断点上传(客户端)
packagecom.cn.csdn.seesun2012.socket;
importjava.io.DataInputStream;
importjava.io.DataOutputStream;
importjava.io.File;
importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.IOException;
importjava.net.Socket;
importjava.net.UnknownHostException;
importjava.util.Timer;
importjava.util.TimerTask;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
/**
* Client端
*功能说明:文件上传(断点)传输
*
* @author CSDN:seesun2012
* @CreateDate 2017年08月18日
* @Override2017年11月07日
* @version 1.1
*/
publicclassFileUpLoadClientextendsSocket{
privateLogger logger=LoggerFactory.getLogger("oaLogger");
privateSocket client;// Socket-客户端
privatestaticlongstatus=0;//进度条
privatebooleanquit=false;//退出
/**
*构造器
*
* @param ip服务端IP地址
* @param report服务端开放的端口
* @throws UnknownHostException
* @throws IOException
*/
publicFileUpLoadClient(String ip,Integer report)throwsUnknownHostException,IOException{
super(ip,report);
this.client=this;
if(client.getLocalPort()>0){
System.out.println("Cliect[port:"+client.getLocalPort()+"]成功连接服务端");
}else{
System.out.println("服务器连接失败");
}
}
publicintsendFile(String filePath){
DataOutputStream dos=null;//上传服务器:输出流
DataInputStream dis=null;//获取服务器:输入流
Long serverLength=-1l;//存储在服务器的文件长度,默认-1
FileInputStream fis=null;//读取文件:输入流
//获取:上传文件
File file=newFile(filePath);
// ====================节点:文件是否存在 ====================
if(file.exists()){
//发送:文件名称、文件长度
try{
dos=newDataOutputStream(client.getOutputStream());
}catch(IOException e2){
logger.error("Socket客户端:1.读取输出流发生错误");
e2.printStackTrace();
}
try{
dos.writeUTF(file.getName());
dos.flush();
dos.writeLong(file.length());
dos.flush();
}catch(IOException e2){
logger.error("Socket客户端:2.向服务器发送文件名、长度发生错误");
e2.printStackTrace();
}
//获取:已上传文件长度
try{
dis=newDataInputStream(client.getInputStream());
}catch(IOException e2){
logger.error("Socket客户端:3.向服务器发送文件名、长度发生错误");
e2.printStackTrace();
}
while(serverLength==-1){
try{
serverLength=dis.readLong();
}catch(IOException e){
logger.error("Socket客户端:4.读取服务端长度发送错误");
e.printStackTrace();
}
}
//读取:需要上传的文件
try{
fis=newFileInputStream(file);
}catch(FileNotFoundException e2){
logger.error("Socket客户端:5.读取本地需要上传的文件失败,请确认文件是否存在");
e2.printStackTrace();
}
//发送:向服务器传输输入流
try{
dos=newDataOutputStream(client.getOutputStream());
}catch(IOException e2){
logger.error("Socket客户端:6.向服务器传输输入流发生错误");
e2.printStackTrace();
}
System.out.println("========开始传输文件 ========");
byte[]bytes=newbyte[1024];
intlength=1024;
longprogress=serverLength;
//设置游标:文件读取的位置
if(serverLength==-1l){
serverLength=0l;
}
try{
fis.skip(serverLength);
}catch(IOException e1){
logger.error("Socket客户端:7.设置游标位置发生错误,请确认文件流是否被篡改");
e1.printStackTrace();
}
try{
while(((length=fis.read(bytes,0,bytes.length))!=-1)&&quit!=true){
dos.write(bytes,0,length);
dos.flush();
progress+=length;
status=(100*progress/file.length());
}
}catch(IOException e){
logger.error("Socket客户端:8.设置游标位置发生错误,请确认文件流是否被篡改");
e.printStackTrace();
}finally{
if(fis!=null)
try{
fis.close();
}catch(IOException e1){
logger.error("Socket客户端:9.关闭读取的输入流异常");
e1.printStackTrace();
}
if(dos!=null)
try{
dos.close();
}catch(IOException e1){
logger.error("Socket客户端:10.关闭发送的输出流异常");
e1.printStackTrace();
}
try{
client.close();
}catch(IOException e){
logger.error("Socket客户端:11.关闭客户端异常");
e.printStackTrace();
}
}
System.out.println("========文件传输成功 ========");
}else{
logger.error("Socket客户端:0.文件不存在");
return-1;
}
return1;
}
/**
*进度条
*/
publicvoidstatusInfo(){
Timer time=newTimer();
time.schedule(newTimerTask(){
longnum=0;
@Override
publicvoidrun(){
if(status>num){
System.out.println("当前进度为:"+status+"%");
num=status;
}
if(status==101){
System.gc();
}
}
},0,100);
}
/**
*退出
*/
publicvoidquit(){
this.quit=true;
try{
this.close();
}catch(IOException e){
System.out.println("服务器关闭发生异常,原因未知");
}
}
}
断点上传(参数设置)
packagecom.cn.csdn.seesun2012.socket;
publicinterfaceFinalVariables{
//服务端IP
publicfinalstaticString SERVER_IP="192.168.1.10010";
//服务端端口
publicfinalstaticintSERVER_PORT=10086;
//开启配置
publicfinalstaticString IS_START_SERVER="instart";
}
断点上传(JavaWeb启动服务端)
packagecom.cn.csdn.seesun2012.socket;
importjava.util.Timer;
importjava.util.TimerTask;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
/**
* Server端
*功能说明:服务端监听开启Servlet
*
* @author CSDN:seesun2012
* @CreateDate 2017年08月18日
* @Override2017年11月07日
* @Override2017年11月14日
* @version 1.3
*/
publicclassSocketServerListenerextendsHttpServlet{
privatestaticfinallongserialVersionUID=-999999999999999999L;
//初始化启动Socket服务
@Override
publicvoidinit()throwsServletException{
super.init();
for(inti=0;i<3;i++){
if("instart".equals(FinalVariables.IS_START_SERVER)){
open();
break;
}
}
}
publicvoidopen(){
Timer timer=newTimer();
timer.schedule(newTimerTask(){
@SuppressWarnings("resource")
@Override
publicvoidrun(){
try{
FileUpLoadServer fileUpLoadServer=newFileUpLoadServer(FinalVariables.SERVER_PORT);
fileUpLoadServer.load();
}catch(Exception e){
e.printStackTrace();
}
}
},3000);
}
}
web.xml配置(跟随项目启动)
index.html
index.jsp
SocketServerListener
com.cn.csdn.seesun2012.socket.SocketServerListener
10
seesun2012