黑马程序员技术交流社区
标题:
【上海校区】webuploader 实现文件断点续传
[打印本页]
作者:
梦缠绕的时候
时间:
2018-7-24 09:42
标题:
【上海校区】webuploader 实现文件断点续传
先去下载webuploader
只需要将下面4个文件添加到工程里面就可以了
<link href="${ctxStatic }/webupload/webuploader.css" type="text/css"
rel
=
"stylesheet"
/>
<script type="text/javascript"
src
=
"${ctxStatic }/webupload/webuploader.nolog.min.js"
></script>
<meta name="decorator" content="default" />
<script type="text/javascript">
$(
document
).ready(function() {
$(
"#startOrStopBtn"
).hide();
var
$list = $(
'#thelist'
),
//文件列表
$btn = $(
'#startOrStopBtn'
),
//开始上传按钮
state =
'pending'
,
//初始按钮状态
uploader;
//uploader对象
var
fileMd5;
//文件唯一标识
var
fileName;
//文件名称
var
oldJindu;
//如果该文件之前上传过 已经上传的进度是多少
var
count=
0
;
//当前正在上传的文件在数组中的下标,一次上传多个文件时使用
var
success=
0
;
//上传成功的文件数
var
filesArr=
new
Array
();
//文件数组:每当有文件被添加进队列的时候 就push到数组中
var
map={};
//key存储文件id,value存储该文件上传过的进度
// 监听分块上传过程中的三个时间点
WebUploader.Uploader.register({
"before-send-file"
:
"beforeSendFile"
,
//整个文件上传前
"before-send"
:
"beforeSend"
,
//每个分片上传前
"after-send-file"
:
"afterSendFile"
,
//分片上传完毕
},
{
//时间点1:所有分块进行上传之前调用此函数
beforeSendFile:function(file){
var
deferred = WebUploader.Deferred();
//1、计算文件的唯一标记fileMd5,用于断点续传 如果.md5File(file)方法里只写一个file参数则计算MD5值会很慢 所以加了后面的参数:10*1024*1024
(
new
WebUploader.Uploader()).md5File(file,
0
,
10
*
1024
*
1024
).progress(function(percentage){
$(
'#'
+file.id ).find(
'p.state'
).text(
'正在读取文件信息...'
);
})
.then(function(val){
$(
'#'
+file.id ).find(
"p.state"
).text(
"成功获取文件信息..."
);
fileMd5=val;
//获取文件信息后进入下一步
deferred.resolve();
});
fileName=file.name;
//为自定义参数文件名赋值
return
deferred.promise();
},
//时间点2:如果有分块上传,则每个分块上传之前调用此函数
beforeSend:function(block){
var
deferred = WebUploader.Deferred();
$.ajax({
type
:
"POST"
,
url
:
"${ctx}/recorder/data/mergeOrCheckChunks"
,
//ajax验证每一个分片
data:{
param
:
"checkChunk"
,
fileName
: fileName,
jindutiao
:$(
"#jindutiao"
).val(),
fileMd5
:fileMd5,
//文件唯一标记
chunk:block.chunk,
//当前分块下标
chunkSize:block.end-block.start
//当前分块大小
},
cache
:
false
,
async
:
false
,
// 同步
timeout:
1000
,
//todo 超时的话,只能认为该分片未上传过
//dataType:"json",
success:function(response){
var
res =
eval
(
'('
+response+
')'
);
if
(res.ifExist){
//分块存在,跳过
deferred.reject();
}
else
{
//分块不存在或不完整,重新发送该分块内容
deferred.resolve();
}
}
});
this
.owner.options.formData.fileMd5 = fileMd5;
deferred.resolve();
return
deferred.promise();
},
//时间点3:所有分块上传成功后调用此函数
afterSendFile:function(){
//如果分块上传成功,则通知后台合并分块
$.ajax({
type
:
"POST"
,
url
:
"${ctx}/recorder/data/mergeOrCheckChunks"
,
//ajax将所有片段合并成整体
data:{
param
:
"mergeChunks"
,
fileName
: fileName,
fileMd5
:fileMd5
},
success
:function(data){
count++;
//每上传完成一个文件 count+1
if
(count<=filesArr.length
-1
){
uploader.upload(filesArr[count].id);
//上传文件列表中的下一个文件
}
//合并成功之后的操作
}
});
}
});
uploader = WebUploader.create({
auto
:
false
,
//是否自动上传
pick : {
id
:
'#picker'
,
label
:
'选择文件'
,
multiple
:
false
},
duplicate
:
false
,
//同一文件是否可重复选择
prepareNextFile:
true
,
// 不压缩image
resize:
false
,
accept
: {
title
:
'ZIP'
,
extensions
:
'zip'
,
mimeTypes
:
'application/x-compressed,application/x-zip-compressed,application/zip,multipart/x-zip'
},
compress
:
null
,
//图片不压缩
chunked :
true
,
//分片
chunkSize :
10
*
1024
*
1024
,
//每片10M
chunkRetry :
3
,
//如果失败,则不重试
threads :
3
,
//上传并发数。允许同时最大上传进程数。
fileNumLimit :
10
,
//验证文件总数量, 超出则不允许加入队列
fileSizeLimit:
6
*
1024
*
1024
*
1024
,
//6G 验证文件总大小是否超出限制, 超出则不允许加入队列
fileSingleSizeLimit:
3
*
1024
*
1024
*
1024
,
//3G 验证单个文件大小是否超出限制, 超出则不允许加入队列
// 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。
disableGlobalDnd :
true
,
// swf文件路径
swf :
'${ctxStatic }/webupload/Uploader.swf'
,
// 文件接收服务端。
server :
'${ctx}/recorder/data/fileSave'
});
// 当有文件添加进来的时候
uploader.on(
'fileQueued'
, function( file ) {
//限制单个文件的大小 超出了提示
if
(file.size>
3
*
1024
*
1024
*
1024
){
$.jBox.tip(
"单个文件大小不能超过3G"
);
return
false
;
}
$(
"#startOrStopBtn"
).show();
//如果一次只能选择一个文件,再次选择替换前一个,就增加如下代码
//清空文件队列
//$list.html("");
//清空文件数组
//filesArr=[];
//将选择的文件添加进文件数组
filesArr.push(file);
success++;
$.ajax({
type
:
"POST"
,
url
:
"${ctx}/recorder/data/selectProgressByFileName"
,
//先检查该文件是否上传过,如果上传过,上传进度是多少
data:{
fileName
: file.name
//文件名
},
cache
:
false
,
async
:
false
,
// 同步
//dataType:"json",
success:function(result){
var
res =
eval
(
'('
+result+
')'
);
//上传过
if
(res.jindutiao>
0
){
//上传过的进度的百分比
oldJindu=res.jindutiao/
100
;
//如果上传过 上传了多少
var
jindutiaoStyle=
"width:"
+res.jindutiao+
"%"
;
$list.append(
'<div id="'
+ file.id +
'" class="item">'
+
'<h4 class="info">'
+ file.name +
'</h4>'
+
'<p class="state">已上传'
+res.jindutiao+
'%</p>'
+
'<a href="javascript:void(0);" class="btn btn-primary file_btn btnRemoveFile">删除</a>'
+
'<div class="progress progress-striped active">'
+
'<div class="progress-bar" role="progressbar" style="'
+jindutiaoStyle+
'">'
+
'</div>'
+
'</div>'
+
'</div>'
);
//将上传过的进度存入map集合
map[file.id]=oldJindu;
}
else
{
//没有上传过
$list.append(
'<div id="'
+ file.id +
'" class="item">'
+
'<h4 class="info">'
+ file.name +
'</h4>'
+
'<p class="state">等待上传...</p>'
+
'<a href="javascript:void(0);" class="btn btn-primary file_btn btnRemoveFile">删除</a>'
+
'</div>'
);
}
}
});
uploader.stop(
true
);
//删除队列中的文件
$(
".btnRemoveFile"
).bind(
"click"
, function() {
var
fileItem = $(
this
).parent();
uploader.removeFile($(fileItem).attr(
"id"
),
true
);
$(fileItem).fadeOut(function() {
$(fileItem).remove();
});
//数组中的文件也要删除
for
(
var
i=
0
;i<filesArr.length;i++){
if
(filesArr
.id==$(fileItem).attr(
"id"
)){
filesArr.splice(i,
1
);
//i是要删除的元素在数组中的下标,1代表从下标位置开始连续删除一个元素
}
}
//隐藏上传按钮
success--;
if
(success ==
0
){
$(
"#startOrStopBtn"
).hide();
}
});
});
//文件上传过程中创建进度条实时显示
uploader.on(
'uploadProgress'
, function(file, percentage) {
var
$li = $(
'#'
+file.id ),
$percent = $li.find(
'.progress .progress-bar'
);
//避免重复创建
if
(!$percent.length){
$percent = $(
'<div class="progress progress-striped active">'
+
'<div class="progress-bar" role="progressbar" style="width: 0%">'
+
'</div>'
+
'</div>'
).appendTo( $li ).find(
'.progress-bar'
);
}
//将实时进度存入隐藏域
$(
"#jindutiao"
).val(
Math
.round(percentage *
100
));
//根据fielId获得当前要上传的文件的进度
var
oldJinduValue = map[file.id];
if
(percentage<oldJinduValue && oldJinduValue!=
1
){
$li.find(
'p.state'
).text(
'上传中'
+
Math
.round(oldJinduValue *
100
) +
'%'
);
$percent.css(
'width'
, oldJinduValue *
100
+
'%'
);
}
else
{
$li.find(
'p.state'
).text(
'上传中'
+
Math
.round(percentage *
100
) +
'%'
);
$percent.css(
'width'
, percentage *
100
+
'%'
);
}
});
//上传成功后执行的方法
uploader.on(
'uploadSuccess'
, function( file ) {
//上传成功去掉进度条
$(
'#'
+file.id).find(
'.progress'
).fadeOut();
//隐藏删除按钮
$(
".btnRemoveFile"
).hide();
//隐藏上传按钮
success--;
if
(success ==
0
){
$(
"#startOrStopBtn"
).hide();
}
$(
'#'
+file.id).find(
'p.state'
).text(
'文件已上传成功'
);
});
//上传出错后执行的方法
uploader.on(
'uploadError'
, function( file ) {
errorUpload=
true
;
$btn.text(
'开始上传'
);
uploader.stop(
true
);
$(
'#'
+file.id).find(
'p.state'
).text(
'上传出错,请检查网络连接'
);
});
//文件上传成功失败都会走这个方法
uploader.on(
'uploadComplete'
, function( file ) {
});
uploader.on(
'all'
, function(type){
if
(type ===
'startUpload'
){
state =
'uploading'
;
}
else
if
(type ===
'stopUpload'
){
state =
'paused'
;
}
else
if
(type ===
'uploadFinished'
){
state =
'done'
;
}
if
(state ===
'uploading'
){
$btn.text(
'暂停上传'
);
}
else
{
$btn.text(
'开始上传'
);
}
});
//上传按钮的onclick时间
$btn.on(
'click'
, function(){
if
(state ===
'uploading'
){
uploader.stop(
true
);
}
else
{
//当前上传文件的文件名
var
currentFileName;
//当前上传文件的文件id
var
currentFileId;
//count=0 说明没开始传 默认从文件列表的第一个开始传
if
(count==
0
){
currentFileName=filesArr[
0
].name;
currentFileId=filesArr[
0
].id;
}
else
{
if
(count<=filesArr.length
-1
){
currentFileName=filesArr[count].name;
currentFileId=filesArr[count].id;
}
}
//先查询该文件是否上传过 如果上传过已经上传的进度是多少
$.ajax({
type
:
"POST"
,
url
:
"${ctx}/recorder/data/selectProgressByFileName"
,
data
:{
fileName
: currentFileName
//文件名
},
cache
:
false
,
async
:
false
,
// 同步
// dataType:"json",
success:function(data){
var
res =
eval
(
'('
+data+
')'
);
//如果上传过 将进度存入map
if
(res.jindutiao>
0
){
map[currentFileId]=res.jindutiao/
100
;
}
//执行上传
uploader.upload(currentFileId);
}
});
}
});
});
</script>
HTML:
<body>
<input id="jindutiao" type="hidden" />
<div id="uploader" class="wu-example">
<div class="btns">
<div id="picker" class="webuploader-container">
<div class="webuploader-pick">选择文件</div>
<div id="rt_rt_1bchdejhrarjdvd11h41eoh1nt1"
style
=
"position: absolute; top: 0px; left: 0px; width: 88px; height: 35px; overflow: hidden; bottom: auto; right: auto;"
>
<input id="file_bp" name="file"
class
=
"webuploader-element-invisible"
type
=
"file"
/> <label
style
=
"opacity: 0; width: 100%; height: 100%; display: block; cursor: pointer; background: rgb(255, 255, 255) none repeat scroll 0% 0%;"
></label>
</div>
</div>
<!-- 文件列表:选择文件后在该div显示 -->
<div id="thelist"
class
=
"uploader-list list-group-item clearfix ng-hide"
></div>
<label class="text-right"
style
=
"font-weight: 100; float: left; margin-left: 15px; width: 144px; margin-right: 15px;"
></label>
<button class="btn m-b-xs btn-sm btn-info btn-addon"
id
=
"startOrStopBtn"
style
=
"padding: 7px 50px; margin-top: 20px;"
>开始上传</button>
</div>
</div>
</body>
后端:
@Controller
@RequestMapping(
value
=
"${adminPath}/recorder/data"
)
public
class
UploadAndDownloadController
extends
BaseController
{
private
static
final String FILE_UPLOAD =
"upload"
;
private
static
final Logger logger = LoggerFactory.getLogger(UploadAndDownloadController.class);
@RequiresPermissions(
"recorder:data:upload"
)
@RequestMapping(
value
=
"uploadList"
)
public String uploadList(HttpServletRequest request, HttpServletResponse response, Model model) {
return
"modules/evrms/uploadAndDownload/fileUpload"
;
}
@RequiresPermissions(
"recorder:data:download"
)
@RequestMapping(
value
=
"downloadList"
)
public String downloadList(HttpServletRequest request, HttpServletResponse response, Model model) {
String fileName = request.getRealPath(
"/"
) +
"/upload/"
;
File destFile =
new
File(fileName);
if
(!destFile.exists()) {
model.addAttribute(
"fileNameList"
,
new
ArrayList<>());
return
"modules/evrms/uploadAndDownload/fileDownload"
;
}
File[] files = ObjectUtils.listOnlyFiles(destFile);
List<File> fileList =
new
ArrayList<>(Arrays.asList(files));
// 将文件按最后修改时间倒序排
Collections.sort(fileList,
new
Comparator<File>() {
@Override
public
int
compare
(File o1, File o2) {
if
(o1.lastModified() < o2.lastModified()) {
return
-1
;
}
return
1
;
}
});
List<String> fileNameList =
new
ArrayList<>();
for
(File file : fileList) {
fileNameList.
add
(file.getName());
}
model.addAttribute(
"fileNameList"
, fileNameList);
return
"modules/evrms/uploadAndDownload/fileDownload"
;
}
@RequiresPermissions(
"recorder:data:upload"
)
@RequestMapping(
value
=
"mergeOrCheckChunks"
)
@ResponseBody
public
String
mergeOrCheckChunks
(HttpServletRequest request, HttpServletResponse response, Model model) {
String param = request.getParameter(
"param"
);
String fileName = request.getParameter(
"fileName"
);
String savePath = request.getRealPath(
"/"
);
// 文件上传的临时文件保存在项目的temp文件夹下
savePath =
new
File(savePath) +
"/upload/"
;
String json = Constants.EMPTY;
if
(param.
equals
(
"mergeChunks"
)) {
// 合并文件
try
{
// 读取目录里的所有文件
File f =
new
File(savePath +
"/"
+ CacheUtils.
get
(FILE_UPLOAD,
"fileName_"
+ fileName));
// 排除目录,只要文件
File[] fileArray = ObjectUtils.listOnlyFiles(f);
// 转成集合,便于排序
List<File> fileList =
new
ArrayList<File>(Arrays.asList(fileArray));
Collections.sort(fileList,
new
Comparator<File>() {
@Override
public
int
compare
(File o1, File o2) {
if
(Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) {
return
-1
;
}
return
1
;
}
});
// 截取文件名的后缀名
int
pointIndex = fileName.lastIndexOf(
"."
);
// 后缀名
String suffix = fileName.substring(pointIndex);
// 合并后的文件
File outputFile =
new
File(savePath +
"/"
+ fileName);
// 创建文件
try
{
outputFile.createNewFile();
}
catch
(IOException e) {
logger.error(
"IO Exception:"
, e);
}
FileChannel outChannel =
new
FileOutputStream(outputFile).getChannel();
// 合并
FileChannel inChannel;
for
(File file : fileList) {
inChannel =
new
FileInputStream(file).getChannel();
try
{
inChannel.transferTo(
0
, inChannel.size(), outChannel);
}
catch
(IOException e) {
logger.error(
"IO Exception:"
, e);
}
try
{
inChannel.close();
}
catch
(IOException e) {
logger.error(
"IO Exception:"
, e);
}
// 删除分片
file.delete();
}
try
{
outChannel.close();
}
catch
(IOException e) {
logger.error(
"IO Exception:"
, e);
}
// 清除文件夹
File tempFile =
new
File(savePath +
"/"
+ CacheUtils.
get
(FILE_UPLOAD,
"fileName_"
+ fileName));
if
(tempFile.isDirectory() && tempFile.exists()) {
tempFile.delete();
}
Map<String, String> resultMap =
new
HashMap<>();
// 将文件的最后上传时间和生成的文件名返回
resultMap.put(
"lastUploadTime"
,
ObjectUtils.toString(CacheUtils.
get
(FILE_UPLOAD,
"lastUploadTime_"
+ fileName)));
resultMap.put(
"pathFileName"
, CacheUtils.
get
(FILE_UPLOAD,
"fileName_"
+ fileName) + suffix);
// 合并成功后删除缓存中的进度信息
CacheUtils.
remove
(FILE_UPLOAD,
"jindutiao_"
+ fileName);
// 合并成功后删除缓存中的最后上传时间,只存没上传完成的
CacheUtils.
remove
(FILE_UPLOAD,
"lastUploadTime_"
+ fileName);
// 合并成功后删除文件名称与该文件上传时生成的存储分片的临时文件夹的名称在缓存中的信息
CacheUtils.
remove
(FILE_UPLOAD,
"fileName_"
+ fileName);
json = JsonMapper.getInstance().toJson(resultMap);
}
catch
(Exception e) {
logger.error(
"file merge failure:"
, e);
}
}
else
if
(param.
equals
(
"checkChunk"
)) {
// 检查当前分块是否上传成功
String fileMd5 = request.getParameter(
"fileMd5"
);
String chunk = request.getParameter(
"chunk"
);
String chunkSize = request.getParameter(
"chunkSize"
);
String jindutiao = request.getParameter(
"jindutiao"
);
// 文件上传的实时进度
try
{
// 将当前进度存入缓存
CacheUtils.put(FILE_UPLOAD,
"jindutiao_"
+ fileName, jindutiao);
// 将系统当前时间转换为字符串
String lastUploadTime = ObjectUtils.dateToString(
new
Date(),
"yyyy-MM-dd HH:mm:ss"
);
// 将最后上传时间以字符串形式存入缓存
CacheUtils.put(FILE_UPLOAD,
"lastUploadTime_"
+ fileName, lastUploadTime);
String tempFileName = ObjectUtils.toString(System.currentTimeMillis());
if
(ObjectUtils.isNull(CacheUtils.
get
(FILE_UPLOAD,
"fileName_"
+ fileName))) {
// 将文件名与该文件上传时生成的存储分片的临时文件夹的名称存入缓存
// 文件上传时生成的存储分片的临时文件夹的名称由MD5和时间戳组成
CacheUtils.put(FILE_UPLOAD,
"fileName_"
+ fileName, fileMd5 + tempFileName);
}
File checkFile =
new
File(
savePath +
"/"
+ CacheUtils.
get
(FILE_UPLOAD,
"fileName_"
+ fileName) +
"/"
+ chunk);
// 检查文件是否存在,且大小是否一致
if
(checkFile.exists() && checkFile.length() == Integer.parseInt(chunkSize)) {
// 上传过
json =
"{\"ifExist\":1}"
;
}
else
{
// 没有上传过
json =
"{\"ifExist\":0}"
;
}
}
catch
(Exception e) {
logger.error(
"file check failure:"
, e);
}
}
return
json;
}
@RequiresPermissions(
"recorder:data:upload"
)
@RequestMapping(
value
=
"fileSave"
)
// 保存上传分片
public void fileSave(HttpServletRequest request, HttpServletResponse response) {
DiskFileItemFactory factory =
new
DiskFileItemFactory();
ServletFileUpload sfu =
new
ServletFileUpload(factory);
sfu.setHeaderEncoding(
"utf-8"
);
String savePath = request.getRealPath(
"/"
);
savePath =
new
File(savePath) +
"/upload/"
;
String fileMd5 =
null
;
// 小于10m的文件FileItem.getFieldName()是得不到chunk的,所以此处不能是空字符串,否则创建文件时会出错
String chunk =
null
;
String fileName =
null
;
try
{
List<FileItem> items = sfu.parseRequest(request);
for
(FileItem item : items) {
// 上传文件的真实名称
fileName = item.getName();
if
(item.isFormField()) {
String fieldName = item.getFieldName();
if
(fieldName.
equals
(
"fileMd5"
)) {
try
{
fileMd5 = item.getString(
"utf-8"
);
}
catch
(UnsupportedEncodingException e) {
logger.error(
"encoding cast failure:"
, e);
}
}
if
(fieldName.
equals
(
"chunk"
)) {
try
{
chunk = item.getString(
"utf-8"
);
}
catch
(UnsupportedEncodingException e) {
logger.error(
"encoding cast failure:"
, e);
}
}
}
else
{
try
{
File file =
new
File(savePath +
"/"
+ CacheUtils.
get
(FILE_UPLOAD,
"fileName_"
+ fileName));
if
(!file.exists()) {
file.mkdir();
}
File chunkFile =
new
File(
savePath +
"/"
+ CacheUtils.
get
(FILE_UPLOAD,
"fileName_"
+ fileName) +
"/"
+ chunk);
FileUtils.copyInputStreamToFile(item.getInputStream(), chunkFile);
}
catch
(Exception e) {
logger.error(
"IO Exception:"
, e);
}
}
}
}
catch
(FileUploadException e) {
logger.error(
"file upload failure:"
, e);
}
}
@RequiresPermissions(
"recorder:data:upload"
)
@RequestMapping(
value
=
"selectProgressByFileName"
)
@ResponseBody
// 当有文件添加进队列时 通过文件名查看该文件是否上传过 上传进度是多少
public String selectProgressByFileName(String fileName) {
String jindutiao = Constants.EMPTY;
if
(ObjectUtils.isNotNull(fileName)) {
jindutiao = ObjectUtils.toString(CacheUtils.
get
(FILE_UPLOAD,
"jindutiao_"
+ fileName));
}
return
"{jindutiao :'"
+ jindutiao +
"'}"
;
}
@RequiresPermissions(
"recorder:data:download"
)
@RequestMapping(
value
=
"downFile"
)
public String downFile(HttpServletResponse response, HttpServletRequest request,
RedirectAttributes redirectAttributes) {
try
{
String fileName = request.getParameter(
"fileName"
);
File file =
new
File(request.getRealPath(
"/"
) +
"/upload/"
+ fileName);
if
(!file.exists()) {
addMessage(redirectAttributes,
"您要下载的资源已被删除!"
);
return
"redirect:"
+ GlobalConfig.getAdminPath() +
"/recorder/data/downloadList/?repage"
;
}
download(response, request, file);
}
catch
(Exception e) {
logger.error(
"file download failure:"
, e);
}
return
null
;
}
private void download(HttpServletResponse response, HttpServletRequest request, File file) {
InputStream
in
=
null
;
ServletOutputStream
out
=
null
;
try
{
int
fSize = Integer.parseInt(String.valueOf(file.length()));
response.setCharacterEncoding(
"utf-8"
);
response.setContentType(
"application/x-download"
);
response.setHeader(
"Accept-Ranges"
,
"bytes"
);
response.setHeader(
"Content-Length"
, String.valueOf(fSize));
// 防止中文乱码
response.setHeader(
"Content-Disposition"
,
"attachment;fileName="
+
new
String(file.getName().getBytes(),
"ISO-8859-1"
));
in
=
new
FileInputStream(file);
long
pos =
0
;
if
(
null
!= request.getHeader(
"Range"
)) {
// 断点续传
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
try
{
pos = Long.parseLong(request.getHeader(
"Range"
).replaceAll(
"bytes="
,
""
).replaceAll(
"-"
,
""
));
}
catch
(NumberFormatException e) {
pos =
0
;
}
}
out
= response.getOutputStream();
String contentRange =
new
StringBuffer(
"bytes "
).append(pos +
""
).append(
"-"
).append((fSize -
1
) +
""
)
.append(
"/"
).append(fSize +
""
).toString();
response.setHeader(
"Content-Range"
, contentRange);
in
.skip(pos);
byte
[] buffer =
new
byte
[
1024
*
10
];
int
length =
0
;
while
((length =
in
.read(buffer,
0
, buffer.length)) !=
-1
) {
out
.write(buffer,
0
, length);
}
}
catch
(Exception e) {
logger.error(
"file download failure:"
, e);
}
finally
{
try
{
if
(
null
!=
out
) {
out
.flush();
out
.close();
}
if
(
null
!=
in
) {
in
.close();
}
}
catch
(IOException e) {
logger.error(
"IO Exception:"
, e);
}
}
}
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2