java断点续传服务端代码

  1 此类核心续传servlet

  2 |||||||||||||||||||

  3 

  4 

  5 import java.io.BufferedOutputStream;

  6 import java.io.File;

  7 import java.io.IOException;

  8 import java.io.OutputStream;

  9 import java.io.RandomAccessFile;

 10 import java.util.concurrent.ConcurrentSkipListMap;

 11 import javax.servlet.ServletException;

 12 import javax.servlet.http.HttpServlet;

 13 import javax.servlet.http.HttpServletRequest;

 14 import javax.servlet.http.HttpServletResponse;

 15 import org.apache.commons.logging.Log;

 16 import org.apache.commons.logging.LogFactory;

 17 import org.springframework.util.Assert;

 18 

 19 //HTTP 断点续传 demo(客户端测试工具:快车、迅雷)

 20 public class MyHttpDownloadServlet extends HttpServlet {

 21      private static final long serialVersionUID = 1L;

 22      final static Log log = LogFactory.getLog(ArcSyncHttpDownloadServlet. class);

 23 

 24      @Override

 25      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

 26             this.doPost(req, resp);

 27      }

 28 

 29      @Override

 30      protected void doPost(HttpServletRequest request, HttpServletResponse response) {

 31            String name = request. getParameter("filename");

 32 

 33             try {

 34                  log.info( "请求下载的连接地址为:" + request.getRequestURL() + "?" + request.getQueryString());

 35                 Assert. hasText(name);

 36            } catch (IllegalArgumentException e) {

 37                  log.error( "请求下载的文件名参数为空!" );

 38                  return;

 39            }

 40            String path = "D:/Install/" + name;

 41             log.info( "文件拼装路径:" + path);

 42            File downloadFile = new File(path);

 43             if (downloadFile.exists()) {

 44                  if (downloadFile.isFile()) {

 45                       if (downloadFile.length() > 0) {

 46                      } else {

 47                             log.info( "请求下载的文件是一个空文件" );

 48                             return;

 49                      }

 50                       if (!downloadFile.canRead()) {

 51                             log.info( "请求下载的文件不是一个可读的文件" );

 52                             return;

 53                      } else {

 54                      }

 55                 } else {

 56                       log.info( "请求下载的文件是一个文件夹" );

 57                       return;

 58                 }

 59            } else {

 60                  log.info( "请求下载的文件不存在!" );

 61                  return;

 62            }

 63 

 64             long fileLength = downloadFile.length(); // 记录文件大小

 65             long pastLength = 0; // 记录已下载文件大小

 66             int rangeSwitch = 0; // 0:从头开始的全文下载;1:从某字节开始的下载(bytes=27000-);2:从某字节开始到某字节结束的下载(bytes=27000-39000)

 67             long toLength = 0; // 记录客户端需要下载的字节段的最后一个字节偏移量(比如bytes=27000-39000,则这个值是为39000)

 68             long contentLength = 0; // 客户端请求的字节总量

 69            String rangeBytes = ""; // 记录客户端传来的形如“bytes=27000-”或者“bytes=27000-39000”的内容

 70            RandomAccessFile raf = null; // 负责读取数据

 71            OutputStream os = null; // 写出数据

 72            OutputStream out = null; // 缓冲

 73             byte b[] = new byte[1024]; // 暂存容器

 74 

 75             if (request.getHeader( "Range") != null) { // 客户端请求的下载的文件块的开始字节

 76                 response.setStatus(javax.servlet.http.HttpServletResponse. SC_PARTIAL_CONTENT);

 77                  log.info( "request.getHeader(\"Range\")=" + request.getHeader("Range" ));

 78                 rangeBytes = request.getHeader("Range" ).replaceAll("bytes=" , "" );

 79                  if (rangeBytes.indexOf( '-') == rangeBytes.length() - 1) {// bytes=969998336-

 80                      rangeSwitch = 1;

 81                      rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf('-'));

 82                      pastLength = Long.parseLong(rangeBytes.trim());

 83                      contentLength = fileLength - pastLength; // 客户端请求的是 969998336 之后的字节

 84                 } else { // bytes=1275856879-1275877358

 85                      rangeSwitch = 2;

 86                      String temp0 = rangeBytes.substring(0, rangeBytes.indexOf('-'));

 87                      String temp2 = rangeBytes.substring(rangeBytes.indexOf('-' ) + 1, rangeBytes.length());

 88                      pastLength = Long. parseLong(temp0.trim()); // bytes=1275856879-1275877358,从第 1275856879 个字节开始下载

 89                      toLength = Long. parseLong(temp2); // bytes=1275856879-1275877358,到第 1275877358 个字节结束

 90                      contentLength = toLength - pastLength; // 客户端请求的是 1275856879-1275877358 之间的字节

 91                 }

 92            } else { // 从开始进行下载

 93                 contentLength = fileLength; // 客户端要求全文下载

 94            }

 95 

 96             /**

 97             * 如果设设置了Content -Length,则客户端会自动进行多线程下载。如果不希望支持多线程,则不要设置这个参数。 响应的格式是: Content - Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节] ServletActionContext.getResponse().setHeader("Content- Length", new Long(file.length() - p).toString());

 98             */

 99            response.reset(); // 告诉客户端允许断点续传多线程连接下载,响应的格式是:Accept-Ranges: bytes

100            response.setHeader( "Accept-Ranges", "bytes" );// 如果是第一次下,还没有断点续传,状态是默认的 200,无需显式设置;响应的格式是:HTTP/1.1 200 OK

101             if (pastLength != 0) {

102                  // 不是从最开始下载,

103                  // 响应的格式是:

104                  // Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]

105                  log.info( "----------------------------不是从开始进行下载!服务器即将开始断点续传..." );

106                  switch (rangeSwitch) {

107                  case 1: { // 针对 bytes=27000- 的请求

108                      String contentRange = new StringBuffer("bytes ").append(new Long(pastLength).toString()).append("-" ).append(new Long(fileLength - 1).toString()).append("/" ).append(new Long(fileLength).toString()).toString();

109                      response.setHeader( "Content-Range", contentRange);

110                       break;

111                 }

112                  case 2: { // 针对 bytes=27000-39000 的请求

113                      String contentRange = rangeBytes + "/" + new Long(fileLength).toString();

114                      response.setHeader( "Content-Range", contentRange);

115                       break;

116                 }

117                  default: {

118                       break;

119                 }

120                 }

121            } else {

122                  // 是从开始下载

123                  log.info( "----------------------------是从开始进行下载!" );

124            }

125 

126             try {

127                 response.addHeader( "Content-Disposition", "attachment; filename=\"" + downloadFile.getName() + "\"");

128                 response.setContentType(CommonUtil. setContentType(downloadFile.getName())); // set the MIME type.

129                 response.addHeader( "Content-Length", String.valueOf(contentLength));

130                 os = response.getOutputStream();

131                 out = new BufferedOutputStream(os);

132                 raf = new RandomAccessFile(downloadFile, "r");

133                  try {

134                       switch (rangeSwitch) {

135                       case 0: { // 普通下载,或者从头开始的下载

136                             // 同1

137                      }

138                       case 1: { // 针对 bytes=27000- 的请求

139                            raf.seek(pastLength); // 形如 bytes=969998336- 的客户端请求,跳过 969998336 个字节

140                             int n = 0;

141                             while ((n = raf.read(b, 0, 1024)) != -1) {

142                                 out.write(b, 0, n);

143                            }

144                             break;

145                      }

146                       case 2: { // 针对 bytes=27000-39000 的请求

147                            raf.seek(pastLength); // 形如 bytes=1275856879-1275877358 的客户端请求,找到第 1275856879 个字节

148                             int n = 0;

149                             long readLength = 0; // 记录已读字节数

150                             while (readLength <= contentLength - 1024) {// 大部分字节在这里读取

151                                 n = raf.read(b, 0, 1024);

152                                 readLength += 1024;

153                                 out.write(b, 0, n);

154                            }

155                             if (readLength <= contentLength) { // 余下的不足 1024 个字节在这里读取

156                                 n = raf.read(b, 0, ( int) (contentLength - readLength));

157                                 out.write(b, 0, n);

158                            }

159                             break;

160                      }

161                       default: {

162                             break;

163                      }

164                      }

165                      out.flush();

166                       log.info( "------------------------------下载结束" );

167                 } catch (IOException ie) {

168                       /**

169                       * 在写数据的时候, 对于 ClientAbortException 之类的异常, 是因为客户端取消了下载,而服务器端继续向浏览器写入数据时, 抛出这个异常,这个是正常的。 尤其是对于迅雷这种吸血的客户端软件, 明明已经有一个线程在读取 bytes=1275856879-1275877358, 如果短时间内没有读取完毕,迅雷会再启第二个、第三个。。。线程来读取相同的字节段, 直到有一个线程读取完毕,迅雷会 KILL 掉其他正在下载同一字节段的线程, 强行中止字节读出,造成服务器抛 ClientAbortException。 所以,我们忽略这种异常

170                       */

171                       // ignore

172                       log.info( "#提醒# 向客户端传输时出现IO异常,但此异常是允许的的,有可能客户端取消了下载,导致此异常,不用关心!" );

173                 }

174            } catch (Exception e) {

175                  log.error(e.getMessage(), e);

176            } finally {

177                  if (out != null) {

178                       try {

179                            out.close();

180                      } catch (IOException e) {

181                             log.error(e.getMessage(), e);

182                      }

183                 }

184                  if (raf != null) {

185                       try {

186                            raf.close();

187                      } catch (IOException e) {

188                             log.error(e.getMessage(), e);

189                      }

190                 }

191            }

192      }

193 }

 

  1 此类是根据扩展名返回ContentType类型

  2 ||||||||||||||||||||||||||||||||||||||

  3 

  4 

  5 public class CommonUtil {

  6 

  7      public static String setContentType(String returnFileName) {

  8            String contentType = "application/octet-stream";

  9             if ( returnFileName.lastIndexOf(".") < 0)

 10                  return contentType;

 11             returnFileName = returnFileName .toLowerCase();

 12             returnFileName = returnFileName.substring(returnFileName .lastIndexOf("." ) + 1);

 13 

 14             if ( returnFileName.equals("html") || returnFileName.equals("htm") || returnFileName .equals("shtml" )) {

 15                 contentType = "text/html";

 16            } else if ( returnFileName.equals("apk")) {

 17                 contentType = "application/vnd.android.package-archive" ;

 18            } else if ( returnFileName.equals("sis")) {

 19                 contentType = "application/vnd.symbian.install";

 20            } else if ( returnFileName.equals("sisx")) {

 21                 contentType = "application/vnd.symbian.install";

 22            } else if ( returnFileName.equals("exe")) {

 23                 contentType = "application/x-msdownload";

 24            } else if ( returnFileName.equals("msi")) {

 25                 contentType = "application/x-msdownload";

 26            } else if ( returnFileName.equals("css")) {

 27                 contentType = "text/css";

 28            } else if ( returnFileName.equals("xml")) {

 29                 contentType = "text/xml";

 30            } else if ( returnFileName.equals("gif")) {

 31                 contentType = "image/gif";

 32            } else if ( returnFileName.equals("jpeg") || returnFileName.equals("jpg")) {

 33                 contentType = "image/jpeg";

 34            } else if ( returnFileName.equals("js")) {

 35                 contentType = "application/x-javascript";

 36            } else if ( returnFileName.equals("atom")) {

 37                 contentType = "application/atom+xml";

 38            } else if ( returnFileName.equals("rss")) {

 39                 contentType = "application/rss+xml";

 40            } else if ( returnFileName.equals("mml")) {

 41                 contentType = "text/mathml";

 42            } else if ( returnFileName.equals("txt")) {

 43                 contentType = "text/plain";

 44            } else if ( returnFileName.equals("jad")) {

 45                 contentType = "text/vnd.sun.j2me.app-descriptor" ;

 46            } else if ( returnFileName.equals("wml")) {

 47                 contentType = "text/vnd.wap.wml";

 48            } else if ( returnFileName.equals("htc")) {

 49                 contentType = "text/x-component";

 50            } else if ( returnFileName.equals("png")) {

 51                 contentType = "image/png";

 52            } else if ( returnFileName.equals("tif") || returnFileName.equals("tiff")) {

 53                 contentType = "image/tiff";

 54            } else if ( returnFileName.equals("wbmp")) {

 55                 contentType = "image/vnd.wap.wbmp";

 56            } else if ( returnFileName.equals("ico")) {

 57                 contentType = "image/x-icon";

 58            } else if ( returnFileName.equals("jng")) {

 59                 contentType = "image/x-jng";

 60            } else if ( returnFileName.equals("bmp")) {

 61                 contentType = "image/x-ms-bmp";

 62            } else if ( returnFileName.equals("svg")) {

 63                 contentType = "image/svg+xml";

 64            } else if ( returnFileName.equals("jar") || returnFileName.equals("var") || returnFileName .equals("ear" )) {

 65                 contentType = "application/java-archive";

 66            } else if ( returnFileName.equals("doc")) {

 67                 contentType = "application/msword";

 68            } else if ( returnFileName.equals("pdf")) {

 69                 contentType = "application/pdf";

 70            } else if ( returnFileName.equals("rtf")) {

 71                 contentType = "application/rtf";

 72            } else if ( returnFileName.equals("xls")) {

 73                 contentType = "application/vnd.ms-excel";

 74            } else if ( returnFileName.equals("ppt")) {

 75                 contentType = "application/vnd.ms-powerpoint";

 76            } else if ( returnFileName.equals("7z")) {

 77                 contentType = "application/x-7z-compressed";

 78            } else if ( returnFileName.equals("rar")) {

 79                 contentType = "application/x-rar-compressed";

 80            } else if ( returnFileName.equals("swf")) {

 81                 contentType = "application/x-shockwave-flash";

 82            } else if ( returnFileName.equals("rpm")) {

 83                 contentType = "application/x-redhat-package-manager" ;

 84            } else if ( returnFileName.equals("der") || returnFileName.equals("pem") || returnFileName .equals("crt" )) {

 85                 contentType = "application/x-x509-ca-cert";

 86            } else if ( returnFileName.equals("xhtml")) {

 87                 contentType = "application/xhtml+xml";

 88            } else if ( returnFileName.equals("zip")) {

 89                 contentType = "application/zip";

 90            } else if ( returnFileName.equals("mid") || returnFileName.equals("midi") || returnFileName .equals("kar" )) {

 91                 contentType = "audio/midi";

 92            } else if ( returnFileName.equals("mp3")) {

 93                 contentType = "audio/mpeg";

 94            } else if ( returnFileName.equals("ogg")) {

 95                 contentType = "audio/ogg";

 96            } else if ( returnFileName.equals("m4a")) {

 97                 contentType = "audio/x-m4a";

 98            } else if ( returnFileName.equals("ra")) {

 99                 contentType = "audio/x-realaudio";

100            } else if ( returnFileName.equals("3gpp") || returnFileName.equals("3gp")) {

101                 contentType = "video/3gpp";

102            } else if ( returnFileName.equals("mp4")) {

103                 contentType = "video/mp4";

104            } else if ( returnFileName.equals("mpeg") || returnFileName.equals("mpg")) {

105                 contentType = "video/mpeg";

106            } else if ( returnFileName.equals("mov")) {

107                 contentType = "video/quicktime";

108            } else if ( returnFileName.equals("flv")) {

109                 contentType = "video/x-flv";

110            } else if ( returnFileName.equals("m4v")) {

111                 contentType = "video/x-m4v";

112            } else if ( returnFileName.equals("mng")) {

113                 contentType = "video/x-mng";

114            } else if ( returnFileName.equals("asx") || returnFileName.equals("asf")) {

115                 contentType = "video/x-ms-asf";

116            } else if ( returnFileName.equals("wmv")) {

117                 contentType = "video/x-ms-wmv";

118            } else if ( returnFileName.equals("avi")) {

119                 contentType = "video/x-msvideo";

120            }

121 

122             return contentType;

123      }

124 

125 }

 

 1 web.xml中servlet标签

 2 ||||||||||||||||||||

 3 

 4 

 5                <servlet>

 6              <description></ description>

 7              <display-name> ArcSyncHttpDownloadServlet</display-name >

 8              <servlet-name> ArcSyncHttpDownloadServlet</servlet-name >

 9              <servlet-class> com.MyHttpDownloadServlet</servlet-class >

10        </servlet>

11        <servlet-mapping>

12              <servlet-name> ArcSyncHttpDownloadServlet</servlet-name >

13              <url-pattern> /http</url-pattern >

14        </servlet-mapping>

 

  1 此类为测试类

  2 |||||||||||||

  3 

  4 

  5 import java.io.BufferedInputStream;

  6 import java.io.File;

  7 import java.io.RandomAccessFile;

  8 import java.io.UnsupportedEncodingException;

  9 import java.net.HttpURLConnection;

 10 import java.net.URL;

 11 import java.net.URLDecoder;

 12 

 13 public class TestDownload {

 14 

 15     /**

 16      * @param args

 17      */

 18     public static void main(String[] args) {

 19         HttpURLConnection httpURLConnection = null;

 20         URL url = null;

 21         BufferedInputStream bis = null;

 22         byte[] buf = new byte[10240];

 23         int size = 0;

 24         String fileName = "a.exe";

 25         String filePath = "C:/Documents and Settings/Administrator/桌面";

 26         StringremoteUrl= "http://10.10.129.99:8080/project/http?filename=a.exe" ;

 27         

 28         // 检查本地文件

 29         RandomAccessFile rndFile = null;

 30         File file = new File(filePath + "\\" + fileName);

 31 //        long remoteFileSize = getRemoteFileSzie(remoteUrl);

 32 //        long nPos = 0;

 33         long localFileSzie =0;

 34         if (file.exists()) {                      

 35            localFileSzie = file.length();

 36         } else {

 37             // 建立文件

 38             try {

 39                 file.createNewFile();

 40             } catch (Exception e) {

 41                 e.printStackTrace();

 42             }          

 43         }

 44       

 45         // 下载文件

 46         try {

 47             url = new URL(remoteUrl);      

 48             httpURLConnection = (HttpURLConnection)url.openConnection();

 49             // 设置User-Agent

 50             httpURLConnection.setRequestProperty( "User-Agent", "Net");

 51             // 设置续传开始

 52             System. out.println( "本地文件大小:" +localFileSzie);

 53             httpURLConnection.setRequestProperty( "Range", "bytes=" + localFileSzie  + "-" );

 54             // 获取输入流

 55             bis = new BufferedInputStream(httpURLConnection.getInputStream());  

 56            

 57             long remoteFileSize = httpURLConnection.getContentLength();

 58             System. out.println( "返回结果码:" +httpURLConnection.getHeaderField("result"));

 59             //System.out.println("文件名:"+httpURLConnection.getHeaderField("Content-Disposition"));

 60                  if (localFileSzie < remoteFileSize) {

 61                       if (localFileSzie==0) {

 62                            System. out.println( "文件不存在,开始下载..." );

 63                      } else{

 64                            System. out.println( "文件续传..." );

 65                      }

 66                 } else {

 67                      System. out.println( "文件存在,重新下载..." );

 68                      file.delete();

 69                       try {

 70                            file.createNewFile();

 71                      } catch (Exception e) {

 72                            e.printStackTrace();

 73                      }

 74                 }

 75            

 76             rndFile = new RandomAccessFile(filePath + "\\" + fileName, "rw");

 77        

 78             rndFile.seek(localFileSzie);

 79             int i = 0;

 80             while ((size = bis.read(buf)) != -1) {

 81                 //if (i > 500) break;              

 82                 rndFile.write(buf, 0, size);

 83                 i++;

 84             }

 85             System. out.println( "i=" + i);

 86             httpURLConnection.disconnect();

 87         } catch (Exception e) {

 88             e.printStackTrace();

 89         }

 90     }

 91 

 92     public static long getRemoteFileSzie(String url) {

 93         long size = 0;

 94         try {

 95             HttpURLConnection httpUrl = (HttpURLConnection)( new URL(url)).openConnection();

 96             size = httpUrl.getContentLength();

 97             httpUrl.disconnect();          

 98         } catch (Exception e) {

 99             e.printStackTrace();

100         }

101         return size;

102     }

103 }

 

 

 

你可能感兴趣的:(java)