转自:http://blog.sina.com.cn/s/blog_667ac0360102eckm.html

package com.ipan.core.controller.web.result;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.lang.StringUtils;
import org.apache.struts2.dispatcher.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.opensymphony.xwork2.ActionInvocation; ///
// 支持断点续传文件输出流
//
// 对StreamResult做了增强,支持断点续传方式(多线程)下载同时也支持普通方式(单线程)下载;
//
// @author iPan
// @version 2014-3-19
//
public class MulitStreamResult extends StreamResult { private static final long serialVersionUID = -256643510497634924L;
protected static Logger LOG = LoggerFactory.getLogger(MulitStreamResult.class);
@Override
protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
// Find the Response in context
HttpServletResponse response = (HttpServletResponse) invocation.getInvocationContext().get(HTTP_RESPONSE);
HttpServletRequest request = (HttpServletRequest) invocation.getInvocationContext().get(HTTP_REQUEST);
// Override any parameters using values on the stack
resolveParamsFromStack(invocation.getStack(), invocation);
String rangeStr = ""; // 范围字符串(比如“bytes=27000-”或者“bytes=27000-39000”的内容)
long fromLength = 0; // 起点长度(比如bytes=27000-39000,则这个值为27000)
long toLength = 0; // 终点长度(比如bytes=27000-39000,则这个值为39000)
long rangeLength = 0; // 响应的字节总量(toLength - fromLength + 1)
OutputStream out = null; // 输出流 if (inputStream == null) {
// Find the inputstream from the invocation variable stack
inputStream = (InputStream) invocation.getStack().findValue(conditionalParse(inputName, invocation));
}
if (inputStream == null) {
String msg = ("Can not find a java.io.InputStream with the name [" + inputName + "] in the invocation stack. " +
"Check the tag specified for this action.");
LOG.error(msg);
throw new IllegalArgumentException(msg);
}
if (contentLength == null || contentLength.length() < 1) {
throw new IllegalArgumentException("支持断点续传时,[Content-Length]不能为空.");
}
// Set the content type
if (contentCharSet != null && !contentCharSet.equals("")) {
response.setContentType(conditionalParse(contentType, invocation) + ";charset=" + contentCharSet);
} else {
response.setContentType(conditionalParse(contentType, invocation));
} // Set the content-disposition
if (contentDisposition != null) {
response.addHeader("Content-Disposition", conditionalParse(contentDisposition, invocation));
}
// Set the cache control headers if neccessary
if (!allowCaching) {
response.addHeader("Pragma", "no-cache");
response.addHeader("Cache-Control", "no-cache");
}
// Set the content length
String _contentLength = conditionalParse(contentLength, invocation);
// 文件长度
int fileLength = Integer.parseInt(_contentLength);
response.setContentLength(fileLength);
// 需要使用断点续传下载
if (isHeadOfRange(request)) {
LOG.debug("断点续传下载.");
// 设置状态 HTTP/1.1 206 Partial Content
response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);
// 表示使用了断点续传(默认是“none”,可以不指定)
response.setHeader("Accept-Ranges", "bytes");
// 设置Content-Range
StringBuilder crBuf = new StringBuilder("bytes ");
rangeStr = request.getHeader("Range").replaceAll("bytes=", "").trim();
if (rangeStr.endsWith("-")) {
LOG.debug("开区间下载.");
rangeStr = StringUtils.substringBefore(rangeStr, "-");
fromLength = Long.parseLong(rangeStr);
rangeLength = fileLength - fromLength + 1;
crBuf.append(rangeStr).append("-").append(fromLength - 1).append("/").append(fileLength);
} else {
LOG.debug("闭区间下载.");
String num1 = StringUtils.substringBefore(rangeStr, "-");
String num2 = StringUtils.substringAfter(rangeStr, "-");
fromLength = Long.parseLong(num1);
toLength = Long.parseLong(num2);
rangeLength = toLength - fromLength + 1;
crBuf.append(rangeStr).append("/").append(fileLength);
}
// Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]
response.setHeader("Content-Range", crBuf.toString());
// 普通下载
} else {
LOG.debug("普通下载.");
// 默认返回 HTTP/1.1 200 OK
rangeLength = fileLength; // 客户端要求全文下载
}
// 输出文件操作
try {
out = response.getOutputStream();
byte[] outBuff = new byte[bufferSize];
int readLen = 0;
// 闭区间处理
if (toLength > 0) {
LOG.debug("闭区间下载开始...");
inputStream.skip(fromLength);
int readBufSize = (int) Math.min(bufferSize, rangeLength);
long pFrom = fromLength;
while (pFrom < toLength) {
readLen = inputStream.read(outBuff, 0, readBufSize);
pFrom += readBufSize;
readBufSize = (int) Math.min(readBufSize, toLength - pFrom + 1);
out.write(outBuff, 0, readLen);
}
// 开区间处理
} else {
LOG.debug("开区间下载开始...");
inputStream.skip(fromLength);
while ((readLen = inputStream.read(outBuff, 0, bufferSize)) != -1) {
out.write(outBuff, 0, readLen);
}
}
} catch (ClientAbortException e) {
// 忽略(迅雷等下载工具,支持多线程下载,但有些线程会被中途取消,导致异常。)
// LOG.debug(e.getMessage(), e);
} catch (SocketException e) {
// 忽略(迅雷等下载工具,支持多线程下载,但有些线程会被中途取消,导致异常。)
// LOG.debug(e.getMessage(), e);
} catch (Exception e) {
// 其他异常记录日志
LOG.error(e.getMessage(), e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
}
}
if (out != null) {
try {
out.flush();
} catch (Exception e1) {
}
try {
out.close();
} catch (Exception e) {
}
}
}
} private static boolean isHeadOfRange(HttpServletRequest request) {
return request.getHeader("Range") != null;
} }

个人补充:功能并未验证是否可行。

转载:Struts2支持断点续传下载实现的更多相关文章

  1. 在ASP.NET中支持断点续传下载大文件(ZT)

    IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag         客户端每次提交 ...

  2. 在ASP.NET中支持断点续传下载大文件

    IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag        客户端每次提交下 ...

  3. 使用curl断点续传下载文件

    办公网络网速不是很好,使用Chrome下载一些软件时不时会中断,恶心的是Chrome居然不支持断点续传下载(为什么chrome的下载不支持断点续传呢?),迅雷自然是不能装的,那怎么办?还好我有大名鼎鼎 ...

  4. 【FTP】FTP文件上传下载-支持断点续传

    Jar包:apache的commons-net包: 支持断点续传 支持进度监控(有时出不来,搞不清原因) 相关知识点 编码格式: UTF-8等; 文件类型: 包括[BINARY_FILE_TYPE(常 ...

  5. ASIHTTPRequest下载示例(支持断点续传)

    一.创建网络请求队列 首先,创建网络请求队列,如下: ASINetworkQueue   *que = [[ASINetworkQueue alloc] init]; self.netWorkQueu ...

  6. ftp 操作,支持断点续传或者继续下载。

    1.ftpclient 类 public class FTPClient:IDisposable { public static object _obj = new object(); #region ...

  7. C# http下载(支持断点续传)

    分享下项目里面自己封装的一个http下载类 功能如下: 1.支持断点续传 2.下载失败自动重试 3.超时等异常处理 using System; using System.Collections.Gen ...

  8. 实现android支持多线程断点续传下载器功能

    多线程断点下载流程图: 多线程断点续传下载原理介绍: 在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度手机端下载数据时难免会出现无信号断线.电量不足等情况,所以需要断点续传功能根据下 ...

  9. 打印 上一主题 下一主题 利用cURL实现单个文件分多段同时下载,支持断点续传(修订版)

      利用cURL实现单个文件分多段同时下载,支持断点续传(修订版) [复制链接] 摘自 http://bbs.chinaunix.net/thread-917952-1-1.html 在ubuntu下 ...

随机推荐

  1. webpack css打包为一个css

    1.安装 npm install extract-text-webpack-plugin --save-dev 2.项目目录: index文件夹下的index.css: body{ backgroun ...

  2. js 实现图片的无缝滚动

      js 实现图片的无缝滚动 CreateTime--2018年3月7日17:18:34 Author:Marydon 测试成功 <!DOCTYPE html> <html> ...

  3. Spring @Autowired、@Resource、@Required、@Component、@Repository、@Service、@Controller注解的用法和作用

    Spring @Autowired,@Resource,@Required注解的用法和作用 Spring中 @Autowired标签与 @Resource标签 的区别 Spring注解@Compone ...

  4. PHP递归方式把一个数组里面的null转换为空字符串”的方法

    在一些接口的调用中,直接查询数据库出来的字段可能为null字段,但是为了简便前端的判断,需要把null转换成空字符串'',这个时候就需要递归的方式进行.直接上代码如下: //递归方式把数组或字符串 n ...

  5. MySQL-EXPLAIN用法详解

    今天做一个订单任务, 其中需要查有无此订单号信息, 由于订单号是个列表, 所以想检测下如下语句的性能(主要在LIMIT 1上) "订单号 IN (订单号列表)" LIMIT 1 然 ...

  6. mosquitto -- 权限配置

    Mosquitto 权限是根据 topic 控制的.类似与目录管理.您可以设定每个用户订阅/发布权限.也可以设定每个用户可访问的topic范围.从而达到权限控制的目的. 这里我们需要我另外一个帖子(用 ...

  7. Memory leak patterns in JavaScript

    Handling circular references in JavaScript applications Plugging memory leaks in JavaScript is easy ...

  8. vue 发送ajax请求

    一. 简介 1.vue本身不支持发送AJAX请求,需要使用vue-resource(vue1.0版本).axios(vue2.0版本)等插件实现 2.axios是一个基于Promise的HTTP请求客 ...

  9. (void)0和0的区别及用法

    (void)0相当于宏NULL,NULL本身的含义为“空”,在c语言代表“不存在.不确定”的含义. 0不能简单的理解为“没有”的意思,在c语言及二进制中,0和1代表的是“一件事物的正反两个方面“,0是 ...

  10. RVM(ruby version manage)安装指南

    一.安装需要3步gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 \c ...