Struts2支持断点续传下载实现

转自: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)