Java大文件分片上传/多线程上传示例

  • Post author:
  • Post category:java


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。在使用前需要配置一下数据库

效果展示:

视频演示:

Web大文件上传下载-jsp-springboot-mysql-示例


windows控件安装

,,

linux-deb控件包安装

,

linux-rpm控件包安装

,

php7测试

,

php5测试

,

vue-cli-测试

,

asp.net-IIS测试

,

asp.net-阿里云(oss)测试

,

asp.net-华为云(obs)测试

,

jsp-springboot测试

,

ActiveX(x86)源码编译

,

ActiveX(x64)源码编译

,

Windows(npapi)源码编译

,

macOS源码编译

,

Linux(x86_64)源码编译

,

Linux(arm)源码编译

,

Linux(mips-uos)源码编译

,

Linux(mips-kylin-涉密环境)源码编译

,

sm4加密传输

,

压缩传输

,

示例下载地址


源代码文档


asp.net源码下载



jsp-springboot源码下载



jsp-eclipse源码下载



jsp-myeclipse源码下载



php源码下载



csharp-winform源码下载



vue-cli源码下载



c++源码下载


详细配置信息及思路