struts断点续传下载

来源:互联网 发布:名不虚传软件下载 编辑:程序博客网 时间:2024/06/01 07:41

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.struts2.ServletActionContext;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.opensymphony.xwork2.ActionSupport;

public class UserAction extends ActionSupport implements ServletRequestAware, ServletResponseAware{
 private String paramName;
 
 /**
  * request 域
  */
 private transient HttpServletRequest request;
 
 /**
  * response 域
  */
 private transient HttpServletResponse response;

 public String getParamName() {
  return paramName;
 }

 public void setParamName(String paramName) {
  this.paramName = paramName;
 }


 public HttpServletRequest getRequest() {
  return request;
 }

 public HttpServletResponse getResponse() {
  return response;
 }

 /**
  * 解析xml文件,dom解析
  * @param request
  * @param response
  * @throws FactoryConfigurationError
  */
 private void getXmlFileInfo(HttpServletRequest request,
   HttpServletResponse response) throws FactoryConfigurationError
 {
  // 创建DocumentBuilderFactory实例,指定DocumentBuilder
  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  try
  {
   DocumentBuilder db = dbf.newDocumentBuilder();

   // 读取文件
   Document doc = db.parse(new File(UserAction.class.getResource(
     "/menu.xml").toURI()));
   // Document doc = db.parse(new File("/menu.xml"));

   // 获取子节点
   NodeList nl = doc.getElementsByTagName("url");
   int len = nl.getLength();

   // 获取节点数
   for (int i = 0; i < len; i++)
   {
    Element eltStu = (Element) nl.item(i);

    Node eltName = eltStu.getElementsByTagName("name").item(0);
    Node eltJspPath = eltStu.getElementsByTagName("path").item(0);
    Node eltIsLog = eltStu.getElementsByTagName("islog").item(0);

    // 获取各属性的值
    String name = eltName.getFirstChild().getNodeValue();
    String jspPath = eltJspPath.getFirstChild().getNodeValue();
    String isLog = eltIsLog.getFirstChild().getNodeValue();
   }
  } catch (ParserConfigurationException e) {
   e.printStackTrace();
  } catch (SAXException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  } catch (URISyntaxException e) {
   e.printStackTrace();
  }
 }
 
 /**
     * 获取用户请求IP地址
     * @return 用户IP地址
     */
    public String getUserRequestIp(HttpServletRequest request)
    {
        // 本地IP地址
        String userIp = "0.0.0.0";
       
        if (request.getHeader("x-forwarded-for") == null)
        {
         userIp = request.getRemoteAddr();
        }
        else
        {
         userIp = request.getHeader("x-forwarded-for");
        }
        return userIp;
    }
   
 
 /**
  * 获取用的的请求数据
  * @return
  */
 public String getUerRequestInfo()
 {
  //获取用户请求IP
  getUserRequestIp(request);
  //页面请求过来的url路径
  String urlPath = request.getRequestURI();
  //获取最后一个‘/’后面的字符串
  String str = urlPath.substring(urlPath.lastIndexOf("/"));
  //解析xml文件
  getXmlFileInfo(request,response);
  //重定向到页面
  //response.sendRedirect(url);
  return SUCCESS;
 }

 /**
  * 文件下载
  * @param filename 要下载的文件名称
  * @throws IOException
  */
 public String downLoadFile(){
  // 下载文件所处于的目录
  File dir = new File(ServletActionContext.getServletContext()
    .getRealPath("upload"));
  File downloadFile = new File(dir, paramName);
  try {
   downloadFileRanges(downloadFile);
   //downloadFileRanges2(downloadFile);
   //fileDownload(downloadFile);
  } catch (IOException e) {
   e.printStackTrace();
  }
        return null;
 }
   
    /**
  * 文件下载(支持断点续传)
  * @param downloadFile
  * @throws IOException
  */
 private void downloadFileRanges(File downloadFile) throws IOException {
  // 要下载的文件大小
  long fileLength = downloadFile.length();

  // 已下载的文件大小
  long pastLength = 0;

  // 是否快车下载,否则为迅雷或其他
  boolean isFlashGet = true;

  // 用于记录需要下载的结束字节数(迅雷或其他下载)
  long lenEnd = 0;

  // 用于记录客户端要求下载的数据范围字串
  String rangeBytes = request.getHeader("Range");

  // 用于随机读取写入文件
  RandomAccessFile raf = null;
  OutputStream os = null;
  OutputStream outPut = null;
  byte b[] = new byte[1024];

  // 如果客户端下载请求中包含了范围
  if (null != rangeBytes)
  {
   // 返回码 206
   response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
   rangeBytes = request.getHeader("Range").replaceAll("bytes=", "");

   // 判断 Range 字串模式
   if (rangeBytes.indexOf('-') == rangeBytes.length() - 1)
   {
    // 无结束字节数,为快车
    isFlashGet = true;
    rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf('-'));
    pastLength = Long.parseLong(rangeBytes.trim());
   }
   else
   {
    // 迅雷下载
    isFlashGet = false;
    String startBytes = rangeBytes.substring(0,
      rangeBytes.indexOf('-'));
    String endBytes = rangeBytes.substring(
      rangeBytes.indexOf('-') + 1, rangeBytes.length());

    // 已下载文件段
    pastLength = Long.parseLong(startBytes.trim());

    // 还需下载的文件字节数(从已下载文件段开始)
    lenEnd = Long.parseLong(endBytes);
   }
  }

  // 通知客户端允许断点续传,响应格式为:Accept-Ranges: bytes
  response.setHeader("Accept-Ranges", "bytes");
  // response.reset();

  // 如果为第一次下载,则状态默认为 200,响应格式为: HTTP/1.1 200 ok
  if (0 != pastLength)
  {
   // 内容范围字串
   String contentRange = "";

   // 响应格式
   // Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]||[文件的总大小]
   if (isFlashGet)
   {
    contentRange = new StringBuffer("bytes")
      .append(new Long(pastLength).toString()).append("-")
      .append(new Long(fileLength - 1).toString())
      .append("/").append(new Long(fileLength).toString())
      .toString();
   }
   else
   {
    contentRange = new StringBuffer(rangeBytes).append("/")
      .append(new Long(fileLength).toString()).toString();
   }
   response.setHeader("Content-Range", contentRange);
  }

  String fileName = getDownloadChineseFileName(paramName);
  response.setHeader("Content-Disposition",
    "attachment;filename=" + fileName + "");

  // 响应的格式是:
  response.setContentType("application/octet-stream");

  response.addHeader("Content-Length", String.valueOf(fileLength));
  try
  {
   os = response.getOutputStream();
   outPut = new BufferedOutputStream(os);
   raf = new RandomAccessFile(downloadFile, "r");
   // 跳过已下载字节
   raf.seek(pastLength);
   if (isFlashGet)
   {
    // 快车等
    int n = 0;
    while ((n = raf.read(b, 0, 1024)) != -1)
    {
     outPut.write(b, 0, n);
    }
   }
   else
   {
    // 迅雷等
    while (raf.getFilePointer() < lenEnd)
    {
     outPut.write(raf.read());
    }
   }
   outPut.flush();
  }
  catch (IOException e)
  {
   /**
    * 在写数据的时候 对于 ClientAbortException 之类的异常
    * 是因为客户端取消了下载,而服务器端继续向浏览器写入数据时, 抛出这个异常,这个是正常的。 尤其是对于迅雷这种吸血的客户端软件。
    * 明明已经有一个线程在读取 bytes=1275856879-1275877358,
    * 如果短时间内没有读取完毕,迅雷会再启第二个、第三个。。。线程来读取相同的字节段, 直到有一个线程读取完毕,迅雷会 KILL
    * 掉其他正在下载同一字节段的线程, 强行中止字节读出,造成服务器抛 ClientAbortException。
    * 所以,我们忽略这种异常
    */
  }
  finally
  {
   if(outPut != null)
   { 
    outPut.close();
   }
   if(raf != null)
   {
    raf.close();
   }
  }
 }
      
    /**
     * 文件下载(支持断点续传)
     * @param file
     * @return
     */
 private void downloadFileRanges2(File file) {
  if (!file.exists())
  {
   // 文件不存在
   return;
  }
  else
  {
   FileInputStream fis = null;
   OutputStream outPut = null;
   try
   {
    fis = new FileInputStream(file);
    response.setHeader("Accept-Ranges", "bytes");
    // 下载开始字节
    long downBegin = 0;
    // 文件大小
    long fileSize = 0;
    fileSize = file.length();
    // 如果是第一次下,还没有断点续传,状态是默认的 200,无需显式设置
    // 客户端请求的下载的文件块的开始字节
    if (request.getHeader("Range") != null)
    {
     // 如果是下载文件的范围而不是全部,向客户端声明支持并开始文件块下载
     response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);
     // 从请求中得到开始的字节
     downBegin = Long.parseLong(request.getHeader("Range")
       .replaceAll("bytes=", "").replaceAll("-", ""));
    }
    // 下载的文件(或块)长度
    response.setHeader("Content-Length", new Long(fileSize
      - downBegin).toString());
    if (downBegin != 0)
    {
     // 不是从最开始下载,
     response.setHeader("Content-Range", "bytes "
       + new Long(downBegin).toString() + "-"
       + new Long(fileSize - 1).toString() + "/"
       + new Long(fileSize).toString());
    }
    // 响应的格式是:
    response.setContentType("application/octet-stream");
    // 为客户端下载指定默认的下载文件名称
    String fileName = getDownloadChineseFileName(paramName);
//    response.setHeader("Content-Disposition",
//      "attachment;filename=" + fileName + "");
    
    response.reset();
          response.addHeader("Content-Disposition", "attachment;filename="
              + java.net.URLEncoder.encode(fileName, "utf-8"));
          response.addHeader("Content-Length", "" + file.length());
         
    fis.skip(downBegin);
    byte[] b = new byte[1024];
    int len;
    outPut = response.getOutputStream();
    while ((len = fis.read(b)) != -1)
    {
     outPut.write(b, 0, len);
     //response.flushBuffer();
    }
    outPut.flush();
    fis.close();
   }
   catch (FileNotFoundException e)
   {
    System.out.println("error");
   }
   catch (IOException e)
   {
    /**
        * 在写数据的时候
        * 对于 ClientAbortException 之类的异常
        * 是因为客户端取消了下载,而服务器端继续向浏览器写入数据时,
        * 抛出这个异常,这个是正常的。
        * 尤其是对于迅雷这种吸血的客户端软件。
        * 明明已经有一个线程在读取 bytes=1275856879-1275877358,
        * 如果短时间内没有读取完毕,迅雷会再启第二个、第三个。。。线程来读取相同的字节段,
        * 直到有一个线程读取完毕,迅雷会 KILL 掉其他正在下载同一字节段的线程,
        * 强行中止字节读出,造成服务器抛 ClientAbortException。
        * 所以,我们忽略这种异常
        */
   }
   finally
   {
    if (fis != null)
    {
     try {
      fis.close();
     }
     catch (IOException e)
     {
      e.printStackTrace();
     }
    }
    if(outPut != null)
    {
     try
     {
      outPut.close();
     }
     catch (IOException e)
     {
      e.printStackTrace();
     }
    }

   }
  }
 }

 /**
     * 如果下载文件名为中文,进行字符编码转换
     * @param paramName
     * @return downloadChineseFileName
     */
 private String getDownloadChineseFileName(String paramName)
 {
  String downloadChineseFileName = "";
  try
  {
   downloadChineseFileName = new String(paramName.getBytes("GBK"),
     "ISO8859-1");
  }
  catch (UnsupportedEncodingException e)
  {
   e.printStackTrace();
  }
  return downloadChineseFileName;
 }
 
 /**
     * 文件下载(不支持断点续传)
     * @param file
     * @throws IOException
     */
    private void fileDownload(File file)
        throws IOException
    {  
        // 以流的形式下载文件。  
        InputStream fis = new BufferedInputStream(new FileInputStream(file));
        byte[] buffer = new byte[fis.available()];
        fis.read(buffer);
        fis.close();
       
        // 清空response  
        response.reset();
        response.addHeader("Content-Disposition", "attachment;filename="
            + java.net.URLEncoder.encode(paramName, "utf-8"));
        response.addHeader("Content-Length", "" + file.length());
        OutputStream os = new BufferedOutputStream(response.getOutputStream());
        response.setContentType("application/octet-stream");
        os.write(buffer);
        os.flush();
        os.close();
    }
   
 @Override
 public void setServletResponse(HttpServletResponse arg0) {
  this.response = arg0;
 }

 @Override
 public void setServletRequest(HttpServletRequest arg0) {
  this.request = arg0;
 }
}

原创粉丝点击