java上传视频进度插件_Java动态显示文件上传进度的简单实现

实现文件上传的进度显示,我们先看看都有哪些问题我们要解决。

1 上传数据的处理进度跟踪

2 进度数据在用户页面的显示

就这么2个问题,

第一个问题,主要是组件的选择

必须支持数据处理侦听或通知的组件。当然,我肯定只用我自己的组件啦。基本原理是

1 使用request.getContentLength() 读取到处理数据的总长度,注意这个长度不等于文件的长度,因为Base64等编码会增加数据量,如果超过了允许的长度,直接返回-1;

2 在每读取一部分数据时(比如一行,或者64K,或者你自定义的字节数),将读取的字节数通知我们的进度跟踪程序。我取名为 UploadListener代码如下

ExpandedBlockStart.gif

ContractedBlock.gif

/** */

/**

6a9c071a08f1dae2d3e1c512000eef41.png

* 处理附件上传的通知。

6a9c071a08f1dae2d3e1c512000eef41.png

6a9c071a08f1dae2d3e1c512000eef41.png

* 各位可以继承这个类,来实现自己的特殊处理。

6a9c071a08f1dae2d3e1c512000eef41.png

*

6a9c071a08f1dae2d3e1c512000eef41.png

*

@author

赵学庆 www.java2000.net

ExpandedBlockEnd.gif

*/

ExpandedBlockStart.gif

ContractedBlock.gif

public

class

UploadListener

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

//

调试模式将在控制台打印出一些数据

6a9c071a08f1dae2d3e1c512000eef41.png

private

boolean

debug;

6a9c071a08f1dae2d3e1c512000eef41.png

6a9c071a08f1dae2d3e1c512000eef41.png

//

总数据字节数

6a9c071a08f1dae2d3e1c512000eef41.png

private

int

total;

6a9c071a08f1dae2d3e1c512000eef41.png

6a9c071a08f1dae2d3e1c512000eef41.png

//

当前已经处理的数据字节数

6a9c071a08f1dae2d3e1c512000eef41.png

private

int

totalCurrent

=

0

;

6a9c071a08f1dae2d3e1c512000eef41.png

6a9c071a08f1dae2d3e1c512000eef41.png

//

延迟,用来调试用,免得速度太快,根本卡看不到进度

6a9c071a08f1dae2d3e1c512000eef41.png

private

int

delay

=

0

;

6a9c071a08f1dae2d3e1c512000eef41.png

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

/** */

/**

6a9c071a08f1dae2d3e1c512000eef41.png

* 处理数据通知的方法。

6a9c071a08f1dae2d3e1c512000eef41.png

6a9c071a08f1dae2d3e1c512000eef41.png

* 保存已经处理的数据。并且在一定的比例进行延迟。默认每1%

6a9c071a08f1dae2d3e1c512000eef41.png

6a9c071a08f1dae2d3e1c512000eef41.png

* 如果不需用延迟,可以删掉内部的代码,加快速度。

6a9c071a08f1dae2d3e1c512000eef41.png

*

6a9c071a08f1dae2d3e1c512000eef41.png

*

@param

size 增加的字节数

ExpandedSubBlockEnd.gif

*/

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

public

void

increaseTotalCurrent(

long

size)

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

this

.totalCurrent

+=

size;

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

try

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

currentRate

=

totalCurrent

*

100

/

total;

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

if

(currentRate

>

lastRate)

...

{

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

if

(delay

>

0

)

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

Thread.sleep(delay);

ExpandedSubBlockEnd.gif

}

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

if

(debug)

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

System.out.println(

"

rate=

"

+

totalCurrent

+

"

/

"

+

total

+

"

/

"

+

(totalCurrent

*

100

/

total));

ExpandedSubBlockEnd.gif

}

6a9c071a08f1dae2d3e1c512000eef41.png

lastRate

=

currentRate;

ExpandedSubBlockEnd.gif

}

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

}

catch

(Exception e)

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

e.printStackTrace();

ExpandedSubBlockEnd.gif

}

ExpandedSubBlockEnd.gif

}

6a9c071a08f1dae2d3e1c512000eef41.png

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

/** */

/**

6a9c071a08f1dae2d3e1c512000eef41.png

* 读取全部自己数

6a9c071a08f1dae2d3e1c512000eef41.png

*

6a9c071a08f1dae2d3e1c512000eef41.png

*

@return

ExpandedSubBlockEnd.gif

*/

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

public

int

getTotal()

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

return

total;

ExpandedSubBlockEnd.gif

}

6a9c071a08f1dae2d3e1c512000eef41.png

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

/** */

/**

6a9c071a08f1dae2d3e1c512000eef41.png

* 读取已经处理的字节数

6a9c071a08f1dae2d3e1c512000eef41.png

*

6a9c071a08f1dae2d3e1c512000eef41.png

*

@return

ExpandedSubBlockEnd.gif

*/

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

public

int

getTotalCurrent()

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

return

totalCurrent;

ExpandedSubBlockEnd.gif

}

6a9c071a08f1dae2d3e1c512000eef41.png

6a9c071a08f1dae2d3e1c512000eef41.png

private

long

lastRate

=

0

;

6a9c071a08f1dae2d3e1c512000eef41.png

6a9c071a08f1dae2d3e1c512000eef41.png

private

long

currentRate

=

0

;

6a9c071a08f1dae2d3e1c512000eef41.png

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

public

int

getDelay()

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

return

delay;

ExpandedSubBlockEnd.gif

}

6a9c071a08f1dae2d3e1c512000eef41.png

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

public

void

setDelay(

int

delay)

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

this

.delay

=

delay;

ExpandedSubBlockEnd.gif

}

6a9c071a08f1dae2d3e1c512000eef41.png

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

public

void

setTotal(

int

total)

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

this

.total

=

total;

ExpandedSubBlockEnd.gif

}

6a9c071a08f1dae2d3e1c512000eef41.png

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

public

boolean

isDebug()

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

return

debug;

ExpandedSubBlockEnd.gif

}

6a9c071a08f1dae2d3e1c512000eef41.png

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

public

void

setDebug(

boolean

debug)

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

this

.debug

=

debug;

ExpandedSubBlockEnd.gif

}

ExpandedBlockEnd.gif

}

None.gif

3 下面我们来看上传的处理部分

None.gif

Upload upload

=

new

Upload(request);

None.gif

//

增加了侦听进度的代码

None.gif

UploadListener uploadListener

=

new

UploadListener();

None.gif

//

这句话我们后面再讨论,这个可是关键

None.gif

session.setAttribute(

"

uploadListener

"

,uploadListener);

None.gif

uploadListener.setDelay(

0

);

None.gif

uploadListener.setDebug(

true

);

None.gif

upload.setUploadListener(uploadListener);

None.gif

upload.parse();

None.gif

//

这句话同样重要,我们后面再讨论

None.gif

session.setAttribute(

"

uploadListener

"

,

null

);

None.gif

4 我们再看上传的表单部分

None.gif

<

script type

=

"

text/javascript

"

>

ExpandedBlockStart.gif

ContractedBlock.gif

function checkForm()

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

$(

"

SHOW_FRAME

"

).src

=

"

link.jsp

"

;

6a9c071a08f1dae2d3e1c512000eef41.png

$(

'

SUBMIT

'

).disabled

=

true

;

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

Ext.MessageBox.show(

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

title:

'

Please wait...

'

,

6a9c071a08f1dae2d3e1c512000eef41.png

msg:

'

Initializing...

'

,

6a9c071a08f1dae2d3e1c512000eef41.png

width:

240

,

6a9c071a08f1dae2d3e1c512000eef41.png

progress:

true

,

6a9c071a08f1dae2d3e1c512000eef41.png

closable:

false

ExpandedSubBlockEnd.gif

}

);

6a9c071a08f1dae2d3e1c512000eef41.png

$(

"

MAIN_FORM

"

).submit();

6a9c071a08f1dae2d3e1c512000eef41.png

return

false

;

ExpandedBlockEnd.gif

}

ExpandedBlockStart.gif

ContractedBlock.gif

function setUploadProcess(total,current)

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

var rate

=

Number(current)

/

Number(total);

6a9c071a08f1dae2d3e1c512000eef41.png

Ext.MessageBox.updateProgress(rate,

'

Uploading...

'

+

current

+

"

/

"

+

total);

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

if

(Number(current)

>=

Number(total))

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

closeUploadProcess();

ExpandedSubBlockEnd.gif

}

ExpandedBlockEnd.gif

}

ExpandedBlockStart.gif

ContractedBlock.gif

function closeUploadProcess()

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

Ext.MessageBox.hide();

ExpandedBlockEnd.gif

}

None.gif

script

>

None.gif

<

iframe name

=

"

ACTION_FRAME

"

id

=

"

ACTION_FRAME

"

width

=

"

0

"

height

=

"

0

"

>

iframe

>

None.gif

<

iframe name

=

"

SHOW_FRAME

"

id

=

"

SHOW_FRAME

"

width

=

"

0

"

height

=

"

0

"

>

iframe

>

None.gif

<

form method

=

"

OST

"

id

=

"

MAIN_FORM

"

onsubmit

=

"

return checkForm()

"

enctype

=

"

multipart/form-data

"

None.gif

action

=

"

uploadFileSave.jsp

"

target

=

"

ACTION_FRAME

"

>

None.gif

<

input type

=

"

file

"

size

=

"

50

"

name

=

"

file

"

>

None.gif

<

input  type

=

"

submit

"

ID

=

"

SUBMIT

"

value

=

"

Upload It

"

>

None.gif

form

>

None.gif

第一个iframe用于提交表单数据,第二个就是我们用来获取处理数据进度信息的。

提交表单很简单,target指向了我们的第一个iframe

我们看一下JS

checkForm 里面第一句就是关键的读取进度信息的页面,我们在第二个iframe里面获得。然后就是弹出进度的显示框,我使用了Ext. 然后提交上传表单

setUploadProcess 用来更新进度框上面的数据,第一个参数是数据总共的大小,第二个参数是已经处理的大小。

closeUploadProcess 关闭进度框

5 最后,我们来看读取进度信息的页面

None.gif

@ page language

=

"

java

"

contentType

=

"

text/html; charset=utf-8

"

pageEncoding

=

"

utf-8

"

%>

None.gif

@include file

=

"

../package.inc.jsp

"

%>

None.gif

None.gif

response.setHeader(

"

ragma

"

,

"

no-cache

"

);

None.gif

response.setHeader(

"

Cache-Control

"

,

"

no-cache

"

);

None.gif

response.setDateHeader(

"

Expires

"

,

0

);

None.gif

response.setBufferSize(

0

);

None.gif

UploadListener uploadListener

=

null

;

ExpandedBlockStart.gif

ContractedBlock.gif

while

(uploadListener

==

null

||

uploadListener.getTotalCurrent()

<=

0

)

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

uploadListener

=

(UploadListener) session.getAttribute(

"

uploadListener

"

);

6a9c071a08f1dae2d3e1c512000eef41.png

out.print(

"

.

"

);

6a9c071a08f1dae2d3e1c512000eef41.png

out.flush();

6a9c071a08f1dae2d3e1c512000eef41.png

Thread.sleep(

10

);

ExpandedBlockEnd.gif

}

None.gif

long

total

=

uploadListener.getTotal();

None.gif

out.println(total);

None.gif

long

current;

None.gif

out.flush();

ExpandedBlockStart.gif

ContractedBlock.gif

while

(

true

)

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

current

=

uploadListener.getTotalCurrent();

ExpandedSubBlockStart.gif

ContractedSubBlock.gif

if

(current

>=

total)

...

{

6a9c071a08f1dae2d3e1c512000eef41.png

break

;

ExpandedSubBlockEnd.gif

}

6a9c071a08f1dae2d3e1c512000eef41.png

out.println(

"

"

+

total

+

"

','

"

+

current

+

"

');

"

);

6a9c071a08f1dae2d3e1c512000eef41.png

out.flush();

6a9c071a08f1dae2d3e1c512000eef41.png

Thread.sleep(

10

);

ExpandedBlockEnd.gif

}

None.gif

%><

script type

=

"

text/javascript

"

>

parent.closeUploadProcess();

script

>

None.gif

其中前面的循环,用来判断是否产生了上传的信息,如果没有则等待。

然后就是读取上传的信息,并计算后生成调用上级窗口的更新进度条的JS, 请注意out.print后面必须跟上out.flush,否则不会持续输出到客户端,也就不会看到连续的进度条变化。

总结:

上面的部分比较乱,我这里总结一下关键点。

1 在上传组件里面,把总大小和当前读取了的大小放到一个类里面,并持续更新,直到处理完毕

2 上传的进度类,放在session里面,供进度读取页面读取

3 进度读取页面,从session里面拿到数据,并返回结果。

有几个疑问解释一下。

1 由于Http协议决定了,必须等request处理完毕才会返回输出,所以不能在upload页面里进行处理进度的显示。我前面测试到1M左右的文件不成功,就是没有考虑到这个问题。所以必须单独用一个GET的程序进行读取

2 读取是一个持续不断的过程,因为上传大文件是很慢的!

3 如果你的应用服务器启用了GZIP压缩,是容器管理的,那么很不幸,因为容易必须拿到所有的数据,至少是一部分数据才会返回,所以造成我们返回的那些很少的字节经常会被截住,造成无法显示上传的连续过程。

解决方法

1) 关闭GZIP, 我想许多人不会这么做

2) 使用自定义的GZIP压缩,判断某些东西(比如URL),对他们不进行压缩处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值