首页 / JAVA / java实现文件的断点续传
java实现文件的断点续传
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了java实现文件的断点续传,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含12278字,纯文字阅读大概需要18分钟。
内容图文
![java实现文件的断点续传](/upload/InfoBanner/zyjiaocheng/1334/32f9672206f7427e808f60076eeba29e.jpg)
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(配置文件,跟随项目启动)
断点上传(服务端)
package com . cn . csdn . seesun2012 . socket ;
import java . io . DataInputStream ;
import java . io . DataOutputStream ;
import java . io . File ;
import java . io . FileOutputStream ;
import java . io . IOException ;
import java . io . RandomAccessFile ;
import java . math . RoundingMode ;
import java . net . ServerSocket ;
import java . net . Socket ;
import java . text . DecimalFormat ;
public class FileUpLoadServer extends ServerSocket {
// 文件大小
private static DecimalFormat df = null ;
// 退出标识
private boolean quit = false ;
static {
// 设置数字格式,保留一位有效小数
df = new DecimalFormat ( "#0.0" );
df . setRoundingMode ( RoundingMode . HALF_UP );
df . setMinimumFractionDigits ( 1 );
df . setMaximumFractionDigits ( 1 );
}
public FileUpLoadServer ( int report ) throws IOException {
super ( report );
}
/**
* 使用线程处理每个客户端传输的文件
*
* @throws Exception
*/
public void load () throws Exception {
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就建立一个新的线程来处理它
new Thread ( new Task ( socket , ip )). start ();
}
}
/**
* 处理客户端传输过来的文件线程类
*/
class Task implements Runnable {
private Socket sk ; // 当前连接
private String ips ; // 当前连接IP址
public Task ( Socket socket , String ip ) {
this . sk = socket ;
this . ips = ip ;
}
public void run () {
Socket socket = sk ; // 重新定义,请不要移出run()方法外部,否则连接两会被重置
String ip = ips ; // 重新定义,同上IP会变
long serverLength = - 1l ; // 定义:存放在服务器里的文件长度,默认没有为-1
char pathChar = File . separatorChar ; // 获取:系统路径分隔符
String panFu = "D:" ; // 路径:存储文件盘符
DataInputStream dis = null ; // 获取:客户端输出流
DataOutputStream dos = null ; // 发送:向客户端输入流
FileOutputStream fos = null ; // 读取:服务器本地文件流
RandomAccessFile rantmpfile = null ; // 操作类:随机读取
try {
// 获取
dis = new DataInputStream ( socket . getInputStream ());
// 发送
dos = new DataOutputStream ( socket . getOutputStream ());
// 定义客户端传过来的文件名
String fileName = "" ;
while ( fileName == "" ) {
// 读取客户端传来的数据
fileName = dis . readUTF ();
System . out . println ( " 服务器获取客户端文件名称:" + fileName );
File file = new File ( 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 = new DataInputStream ( socket . getInputStream ());
// 文件名和长度
long fileLength = dis . readLong ();
File directory = new File ( panFu + pathChar + "receive" + pathChar + "" + ip + pathChar );
if (! directory . exists ()) {
directory . mkdirs ();
}
int length = 0 ;
byte [] bytes = new byte [ 1024 ];
File file = new File ( directory . getAbsolutePath () + pathChar + fileName );
if (! file . exists ()) {
// 不存在
fos = new FileOutputStream ( file );
// 开始接收文件
while (( length = dis . read ( bytes , 0 , bytes . length )) != - 1 ) {
fos . write ( bytes , 0 , length );
fos . flush ();
}
} else {
// 存储在服务器中的文件长度
long fileSize = file . length (), pointSize = 0 ;
// 判断是否已下载完成
if ( fileLength > fileSize ) {
// 断点下载
pointSize = fileSize ;
} else {
// 重新下载
file . delete ();
file . createNewFile ();
}
rantmpfile = new RandomAccessFile ( 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
*/
public String getFormatFileSize ( long length ) {
double size = (( double ) length ) / ( 1 << 30 );
if ( size >= 1 ) {
return df . format ( size ) + "GB" ;
}
size = (( double ) length ) / ( 1 << 20 );
if ( size >= 1 ) {
return df . format ( size ) + "MB" ;
}
size = (( double ) length ) / ( 1 << 10 );
if ( size >= 1 ) {
return df . format ( size ) + "KB" ;
}
return length + "B" ;
}
/**
* 退出
*/
public void quit () {
this . quit = true ;
try {
this . close ();
} catch ( IOException e ) {
System . out . println ( " 服务器关闭发生异常,原因未知" );
}
}
}
断点上传(客户端)
package com . cn . csdn . seesun2012 . socket ;
import java . io . DataInputStream ;
import java . io . DataOutputStream ;
import java . io . File ;
import java . io . FileInputStream ;
import java . io . FileNotFoundException ;
import java . io . IOException ;
import java . net . Socket ;
import java . net . UnknownHostException ;
import java . util . Timer ;
import java . util . TimerTask ;
import org . slf4j . Logger ;
import org . slf4j . LoggerFactory ;
/**
* Client 端<br><br>
* 功能说明:文件上传(断点)传输
*
* @author CSDN:seesun2012
* @CreateDate 2017 年08月18日
* @Override 2017 年11月07日
* @version 1.1
*/
public class FileUpLoadClient extends Socket {
private Logger logger = LoggerFactory . getLogger ( "oaLogger" );
private Socket client ; // Socket- 客户端
private static long status = 0 ; // 进度条
private boolean quit = false ; // 退出
/**
* 构造器
*
* @param ip 服务端IP地址
* @param report 服务端开放的端口
* @throws UnknownHostException
* @throws IOException
*/
public FileUpLoadClient ( String ip , Integer report ) throws UnknownHostException , IOException {
super ( ip , report );
this . client = this ;
if ( client . getLocalPort ()> 0 ) {
System . out . println ( "Cliect[port:" + client . getLocalPort () + "] 成功连接服务端" );
} else {
System . out . println ( " 服务器连接失败" );
}
}
public int sendFile ( String filePath ) {
DataOutputStream dos = null ; // 上传服务器:输出流
DataInputStream dis = null ; // 获取服务器:输入流
Long serverLength = - 1l ; // 存储在服务器的文件长度,默认-1
FileInputStream fis = null ; // 读取文件:输入流
// 获取:上传文件
File file = new File ( filePath );
// ==================== 节点:文件是否存在 ====================
if ( file . exists ()) {
// 发送:文件名称、文件长度
try {
dos = new DataOutputStream ( 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 = new DataInputStream ( 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 = new FileInputStream ( file );
} catch ( FileNotFoundException e2 ) {
logger . error ( "Socket 客户端:5.读取本地需要上传的文件失败,请确认文件是否存在" );
e2 . printStackTrace ();
}
// 发送:向服务器传输输入流
try {
dos = new DataOutputStream ( client . getOutputStream ());
} catch ( IOException e2 ) {
logger . error ( "Socket 客户端:6.向服务器传输输入流发生错误" );
e2 . printStackTrace ();
}
System . out . println ( "======== 开始传输文件 ========" );
byte [] bytes = new byte [ 1024 ];
int length = 1024 ;
long progress = 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 ;
}
return 1 ;
}
/**
* 进度条
*/
public void statusInfo (){
Timer time = new Timer ();
time . schedule ( new TimerTask () {
long num = 0 ;
@Override
public void run () {
if ( status > num ) {
System . out . println ( " 当前进度为:" + status + "%" );
num = status ;
}
if ( status == 101 ) {
System . gc ();
}
}
}, 0 , 100 );
}
/**
* 退出
*/
public void quit () {
this . quit = true ;
try {
this . close ();
} catch ( IOException e ) {
System . out . println ( " 服务器关闭发生异常,原因未知" );
}
}
}
断点上传(参数设置)
package com . cn . csdn . seesun2012 . socket ;
public interface FinalVariables {
// 服务端IP
public final static String SERVER_IP = "192.168.1.10010" ;
// 服务端端口
public final static int SERVER_PORT = 10086 ;
// 开启配置
public final static String IS_START_SERVER = "instart" ;
}
断点上传(JavaWeb启动服务端)
package com . cn . csdn . seesun2012 . socket ;
import java . util . Timer ;
import java . util . TimerTask ;
import javax . servlet . ServletException ;
import javax . servlet . http . HttpServlet ;
/**
* Server 端<br><br>
* 功能说明:服务端监听开启Servlet
*
* @author CSDN:seesun2012
* @CreateDate 2017 年08月18日
* @Override 2017 年11月07日
* @Override 2017 年11月14日
* @version 1.3
*/
public class SocketServerListener extends HttpServlet {
private static final long serialVersionUID = - 999999999999999999L ;
// 初始化启动Socket服务
@Override
public void init () throws ServletException {
super . init ();
for ( int i = 0 ; i < 3 ; i ++){
if ( "instart" . equals ( FinalVariables . IS_START_SERVER )) {
open ();
break ;
}
}
}
public void open (){
Timer timer = new Timer ();
timer . schedule ( new TimerTask () {
@SuppressWarnings ( "resource" )
@Override
public void run () {
try {
FileUpLoadServer fileUpLoadServer = new FileUpLoadServer ( FinalVariables . SERVER_PORT );
fileUpLoadServer . load ();
} catch ( Exception e ) {
e . printStackTrace ();
}
}
}, 3000 );
}
}
web.xml配置(跟随项目启动)
<? xml version = "1.0" encoding = "UTF-8" ?>
<web-app>
<welcome-file-list>
<welcome-file> index.html </welcome-file>
<welcome-file> index.jsp </welcome-file>
</welcome-file-list>
<servlet>
<servlet-name> SocketServerListener </servlet-name>
<servlet-class> com.cn.csdn.seesun2012.socket.SocketServerListener </servlet-class>
<load-on-startup> 10 </load-on-startup>
</servlet>
<display-name> seesun2012 </display-name>
</web-app>
目前能够支持MySQL,Oracle,SQL。在使用前需要配置一下数据库,可以参考我写的这篇文章: http://blog.ncmem.com/wordpress/2019/08/12/java-http%E5%A4%A7%E6%96%87%E4%BB%B6%E6%96%AD%E7%82%B9%E7%BB%AD%E4%BC%A0%E4%B8%8A%E4%BC%A0/
原文:https://www.cnblogs.com/songsu/p/11797380.html
内容总结
以上是互联网集市为您收集整理的java实现文件的断点续传全部内容,希望文章能够帮你解决java实现文件的断点续传所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。