java实现文件断点续传下载功能

时间:2022-05-06 22:59:01

本文实例为大家分享了java断点续传下载的代码,供大家参考,具体内容如下

1. Java代码    

?
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//实现文件下载功能
  public String downloadFile(){
    File dir = new File(filepath);//获取文件路劲
    if(!dir.exists()) {
      System.out.println("文件路径错误");
      log.debug("文件路径错误");
      return "failed";// 判断文件或文件夹是否存在
    }
     File downloadFile = new File(dir, filename);//在指定目录下查找文件
     if(!downloadFile.isFile()){
       System.out.println("文件不存在");
        log.debug("文件不存在");
        return "failed";// 判断文件或文件夹是否存在
     }
     try {
      downloadFileRanges(downloadFile);
     } catch(ClientAbortException e){
       System.out.println("连接被终止");
       log.debug("连接被终止");
     } catch (IOException e) {
      e.printStackTrace();
     }
    return null;
   }
   
  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(filename);
     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();
      }
     }
     }
   
   
   
  private String getDownloadChineseFileName(String paramName)
   {
   String downloadChineseFileName = "";
   try
   {
    downloadChineseFileName = new String(paramName.getBytes("GBK"),
     "ISO8859-1");
   }
   catch (UnsupportedEncodingException e)
   {
    e.printStackTrace();
   }
   return downloadChineseFileName;
   }
   
   
   
  public String getFilepath() {
    return filepath;
  }
  public void setFilepath(String filepath) {
    this.filepath = filepath;
  }
  public String getFilename() {
    return filename;
  }
  public void setFilename(String filename) {
    this.filename = filename;
  }
  public HttpServletRequest getRequest() {
   return request;
   }
   public HttpServletResponse getResponse() {
   return response;
   }

2. struts部分    

复制代码 代码如下:
<action name="downloadFile" class="downloadFileAction" method="downloadFile">
   <result name="failed" type="redirectAction">showDownloadFileNameList</result>
</action>

 

 

3. jsp部分    

复制代码 代码如下:
<td><a href="downloadFile?filename=${fileMap.key }&&filepath=${fileMap.value }">文件下载</a></td>