Java编程实现服务器端支持断点续传的方法(可支持快车、迅雷)

时间:2021-11-26 22:57:44

本文实例讲述了Java编程实现服务器端支持断点续传的方法。分享给大家供大家参考,具体如下:

大家知道Tomcat之流对静态资源可以实现断点续传支持,但是如果是一个被控制的流,如有权限控制,或下载地址仅是个代理的时候,这时候需要自己实现断点续传的支持,小弟不才,这里提供基本断点续传[a-,-b,a-b]的简单实现,经验证,可支持迅雷7和火狐的多次断点续传。现贴出代码,大家共同分享:

Servlet

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.bsteel.cloud.storage.servlet.base.BaseServlet;
import com.bsteel.cloud.storage.utils.FileUtil;
/**
 * 文件下载(支持断点续传【迅雷\快车\旋风\Firefox\Chrome】)
 * @author jdkleo
 *
 */
public class FileIoServlet extends BaseServlet {
 private static final long serialVersionUID = 1L;
 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {
  this.doPost(req, resp);
 }
 @Override
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
  download(request,response);
 }
 /**
  * 文件下载
  * @param request
  * @param response
  * @throws UnsupportedEncodingException
  */
 private void download(HttpServletRequest request,HttpServletResponse response) throws UnsupportedEncodingException {
  File downloadFile = getFile(request);
  long pos = FileUtil.headerSetting(downloadFile, request, response);
//  log.info("跳过"+pos);
  ServletOutputStream os = null;
  BufferedOutputStream out = null;
  RandomAccessFile raf = null;
  byte b[] = new byte[1024];//暂存容器
  try {
   os = response.getOutputStream();
   out = new BufferedOutputStream(os);
   raf = new RandomAccessFile(downloadFile, "r");
   raf.seek(pos);
   try {
    int n = 0;
    while ((n = raf.read(b, 0, 1024)) != -1) {
     out.write(b, 0, n);
    }
    out.flush();
   } catch(IOException ie) {
   }
  } catch (Exception e) {
   log.error(e.getMessage(), e);
  } finally {
   if (out != null) {
    try {
     out.close();
    } catch (IOException e) {
     log.error(e.getMessage(), e);
    }
   }
   if (raf != null) {
    try {
     raf.close();
    } catch (IOException e) {
     log.error(e.getMessage(), e);
    }
   }
  }
 }
 private File getFile(HttpServletRequest request) throws UnsupportedEncodingException {
  String uriStr = request.getParameter("uri");
  if (null != uriStr){
    uriStr = URLDecoder.decode(uriStr,"UTF-8");
    if (uriStr.startsWith("file://")){
     uriStr = uriStr.substring(7);
     return new File(uriStr);
    }else if (uriStr.startsWith("hbase://")){
     try {
      return new File(new URI(uriStr));
     } catch (URISyntaxException e) {
      log.error(e.getMessage(),e);
     }
    }
  }
  throw new RuntimeException("it's not a real uri");
 }
}

Range支持

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import java.io.File;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 文件处理工具
 * @author jdkleo
 *
 */
public class FileUtil {
 /**
  * 断点续传支持
  * @param file
  * @param request
  * @param response
  * @return 跳过多少字节
  */
 public static long headerSetting(File file,HttpServletRequest request, HttpServletResponse response) {
  long len = file.length();//文件长度
  if ( null == request.getHeader("Range") ){
   setResponse(new RangeSettings(len),file.getName(),response);
   return 0;
  }
  String range = request.getHeader("Range").replaceAll("bytes=", "");
  RangeSettings settings = getSettings(len,range);
  setResponse(settings,file.getName(),response);
  return settings.getStart();
 }
 private static void setResponse(RangeSettings settings,String fileName, HttpServletResponse response) {
  response.addHeader("Content-Disposition", "attachment; filename=\"" + IoUtil.toUtf8String(fileName) + "\"");
  response.setContentType( IoUtil.setContentType(fileName));// set the MIME type.
  if (!settings.isRange())
  {
   response.addHeader("Content-Length", String.valueOf(settings.getTotalLength()));
  }
  else
  {
   long start = settings.getStart();
   long end = settings.getEnd();
   long contentLength = settings.getContentLength();
   response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);
  response.addHeader("Content-Length", String.valueOf(contentLength));
  String contentRange = new StringBuffer("bytes ").append(start).append("-").append(end).append("/").append(settings.getTotalLength()).toString();
  response.setHeader("Content-Range", contentRange);
  }
 }
 private static RangeSettings getSettings(long len, String range) {
  long contentLength = 0;
  long start = 0;
  long end = 0;
  if (range.startsWith("-"))// -500,最后500个
  {
    contentLength = Long.parseLong(range.substring(1));//要下载的量
    end = len-1;
    start = len - contentLength;
  }
  else if (range.endsWith("-"))//从哪个开始
  {
   start = Long.parseLong(range.replace("-", ""));
   end = len -1;
   contentLength = len - start;
  }
  else//从a到b
  {
   String[] se = range.split("-");
   start = Long.parseLong(se[0]);
   end = Long.parseLong(se[1]);
   contentLength = end-start+1;
  }
  return new RangeSettings(start,end,contentLength,len);
 }
}

Range封装

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class RangeSettings{
  private long start;
  private long end;
  private long contentLength;
  private long totalLength;
  private boolean range;
  public RangeSettings(){
   super();
  }
  public RangeSettings(long start, long end, long contentLength,long totalLength) {
   this.start = start;
   this.end = end;
   this.contentLength = contentLength;
   this.totalLength = totalLength;
   this.range = true;
  }
  public RangeSettings(long totalLength) {
   this.totalLength = totalLength;
  }
  public long getStart() {
   return start;
  }
  public void setStart(long start) {
   this.start = start;
  }
  public long getEnd() {
   return end;
  }
  public void setEnd(long end) {
   this.end = end;
  }
  public long getContentLength() {
   return contentLength;
  }
  public void setContentLength(long contentLength) {
   this.contentLength = contentLength;
  }
  public long getTotalLength() {
   return totalLength;
  }
  public void setTotalLength(long totalLength) {
   this.totalLength = totalLength;
  }
  public boolean isRange() {
   return range;
  }
}

IO流相关处理工具类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import java.io.InputStream;
public class IoUtil {
 public static String setContentType(String returnFileName){
  String contentType = "application/octet-stream";
  if (returnFileName.lastIndexOf(".") < 0)
   return contentType;
  returnFileName = returnFileName.toLowerCase();
  returnFileName = returnFileName.substring(returnFileName.lastIndexOf(".")+1);
  if (returnFileName.equals("html") || returnFileName.equals("htm") || returnFileName.equals("shtml")){
   contentType = "text/html";
  } else if (returnFileName.equals("css")){
   contentType = "text/css";
  } else if (returnFileName.equals("xml")){
   contentType = "text/xml";
  } else if (returnFileName.equals("gif")){
   contentType = "image/gif";
  } else if (returnFileName.equals("jpeg") || returnFileName.equals("jpg")){
   contentType = "image/jpeg";
  } else if (returnFileName.equals("js")){
   contentType = "application/x-javascript";
  } else if (returnFileName.equals("atom")){
   contentType = "application/atom+xml";
  } else if (returnFileName.equals("rss")){
   contentType = "application/rss+xml";
  } else if (returnFileName.equals("mml")){
   contentType = "text/mathml";
  } else if (returnFileName.equals("txt")){
   contentType = "text/plain";
  } else if (returnFileName.equals("jad")){
   contentType = "text/vnd.sun.j2me.app-descriptor";
  } else if (returnFileName.equals("wml")){
   contentType = "text/vnd.wap.wml";
  } else if (returnFileName.equals("htc")){
   contentType = "text/x-component";
  } else if (returnFileName.equals("png")){
   contentType = "image/png";
  } else if (returnFileName.equals("tif") || returnFileName.equals("tiff")){
   contentType = "image/tiff";
  } else if (returnFileName.equals("wbmp")){
   contentType = "image/vnd.wap.wbmp";
  } else if (returnFileName.equals("ico")){
   contentType = "image/x-icon";
  } else if (returnFileName.equals("jng")){
   contentType = "image/x-jng";
  } else if (returnFileName.equals("bmp")){
   contentType = "image/x-ms-bmp";
  } else if (returnFileName.equals("svg")){
   contentType = "image/svg+xml";
  } else if (returnFileName.equals("jar") || returnFileName.equals("var") || returnFileName.equals("ear")){
   contentType = "application/java-archive";
  } else if (returnFileName.equals("doc")){
   contentType = "application/msword";
  } else if (returnFileName.equals("pdf")){
   contentType = "application/pdf";
  } else if (returnFileName.equals("rtf")){
   contentType = "application/rtf";
  } else if (returnFileName.equals("xls")){
   contentType = "application/vnd.ms-excel";
  } else if (returnFileName.equals("ppt")){
   contentType = "application/vnd.ms-powerpoint";
  } else if (returnFileName.equals("7z")){
   contentType = "application/x-7z-compressed";
  } else if (returnFileName.equals("rar")){
   contentType = "application/x-rar-compressed";
  } else if (returnFileName.equals("swf")){
   contentType = "application/x-shockwave-flash";
  } else if (returnFileName.equals("rpm")){
   contentType = "application/x-redhat-package-manager";
  } else if (returnFileName.equals("der") || returnFileName.equals("pem") || returnFileName.equals("crt")){
   contentType = "application/x-x509-ca-cert";
  } else if (returnFileName.equals("xhtml")){
   contentType = "application/xhtml+xml";
  } else if (returnFileName.equals("zip")){
   contentType = "application/zip";
  } else if (returnFileName.equals("mid") || returnFileName.equals("midi") || returnFileName.equals("kar")){
   contentType = "audio/midi";
  } else if (returnFileName.equals("mp3")){
   contentType = "audio/mpeg";
  } else if (returnFileName.equals("ogg")){
   contentType = "audio/ogg";
  } else if (returnFileName.equals("m4a")){
   contentType = "audio/x-m4a";
  } else if (returnFileName.equals("ra")){
   contentType = "audio/x-realaudio";
  } else if (returnFileName.equals("3gpp") || returnFileName.equals("3gp")){
   contentType = "video/3gpp";
  } else if (returnFileName.equals("mp4") ){
   contentType = "video/mp4";
  } else if (returnFileName.equals("mpeg") || returnFileName.equals("mpg") ){
   contentType = "video/mpeg";
  } else if (returnFileName.equals("mov")){
   contentType = "video/quicktime";
  } else if (returnFileName.equals("flv")){
   contentType = "video/x-flv";
  } else if (returnFileName.equals("m4v")){
   contentType = "video/x-m4v";
  } else if (returnFileName.equals("mng")){
   contentType = "video/x-mng";
  } else if (returnFileName.equals("asx") || returnFileName.equals("asf")){
   contentType = "video/x-ms-asf";
  } else if (returnFileName.equals("wmv")){
   contentType = "video/x-ms-wmv";
  } else if (returnFileName.equals("avi")){
   contentType = "video/x-msvideo";
  }
  return contentType;
 }
 // UTF8转码
 public static String toUtf8String(String s) {
  StringBuffer sb = new StringBuffer();
  int len = s.toCharArray().length;
  for (int i = 0; i < len; i++) {
   char c = s.charAt(i);
   if (c >= 0 && c <= 255) {
    sb.append(c);
   } else {
    byte[] b;
    try {
     b = Character.toString(c).getBytes("utf-8");
    } catch (Exception ex) {
     System.out.println(ex);
     b = new byte[0];
    }
    for (int j = 0; j < b.length; j++) {
     int k = b[j];
     if (k < 0)
      k += 256;
     sb.append("%" + Integer.toHexString(k).toUpperCase());
    }
   }
  }
  String s_utf8 = sb.toString();
  sb.delete(0, sb.length());
  sb.setLength(0);
  sb = null;
  return s_utf8;
 }
 public static InputStream skipFully(InputStream in,long howMany)throws Exception{
 long remainning = howMany;
 long len = 0;
 while(remainning>0){
  len = in.skip(len);
  remainning -= len;
 }
 return in;
 }
}

注有些类比如IoUtil方法来自于CSDN的网友总结,另外此类还不支持多Range配置如[a-b,c-d,-e]等。

希望本文所述对大家Java程序设计有所帮助。