zoukankan      html  css  js  c++  java
  • 转载: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;
    }
    
    }
    

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

  • 相关阅读:
    Java实现点击导出excel页面遮罩屏蔽,下载完成后解除遮罩
    文档API生成神器SandCastle使用心得
    Stimulsoft报表操作笔记(一):统计
    在线office文档编辑NTKO使用心得
    oracle与sqlserver利用函数生成年月日加流水号
    解决同一页面中两个iframe互相调用jquery,js函数
    jquery配合.NET实现点击指定绑定数据并且能够一键下载
    页面的div中有滚动条,js实现刷新页面后回到记录时滚动条的位置
    关于datagrid中控件利用js调用后台方法事件的问题
    关于datagrid中数据条件颜色问题
  • 原文地址:https://www.cnblogs.com/ribavnu/p/3701401.html
Copyright © 2011-2022 走看看