java實(shí)現(xiàn)文件斷點(diǎn)續(xù)傳下載功能
本文實(shí)例為大家分享了java斷點(diǎn)續(xù)傳下載的代碼,供大家參考,具體內(nèi)容如下
1. Java代碼
//實(shí)現(xiàn)文件下載功能 public String downloadFile(){ File dir = new File(filepath);//獲取文件路勁 if(!dir.exists()) { System.out.println("文件路徑錯(cuò)誤"); log.debug("文件路徑錯(cuò)誤"); 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; // 是否快車(chē)下載,否則為迅雷或其他 boolean isFlashGet = true; // 用于記錄需要下載的結(jié)束字節(jié)數(shù)(迅雷或其他下載) long lenEnd = 0; // 用于記錄客戶端要求下載的數(shù)據(jù)范圍字串 String rangeBytes = request.getHeader("Range"); //用于隨機(jī)讀取寫(xiě)入文件 RandomAccessFile raf = null; OutputStream os = null; OutputStream outPut = null; byte b[] = new byte[1024]; // 如果客戶端下載請(qǐng)求中包含了范圍 if (null != rangeBytes) { // 返回碼 206 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); rangeBytes = request.getHeader("Range").replaceAll("bytes=", ""); // 判斷 Range 字串模式 if (rangeBytes.indexOf('-') == rangeBytes.length() - 1) { // 無(wú)結(jié)束字節(jié)數(shù),為快車(chē) 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()); // 還需下載的文件字節(jié)數(shù)(從已下載文件段開(kāi)始) lenEnd = Long.parseLong(endBytes); } } // 通知客戶端允許斷點(diǎn)續(xù)傳,響應(yīng)格式為:Accept-Ranges: bytes response.setHeader("Accept-Ranges", "bytes"); // response.reset(); // 如果為第一次下載,則狀態(tài)默認(rèn)為 200,響應(yīng)格式為: HTTP/1.1 200 ok if (0 != pastLength) { // 內(nèi)容范圍字串 String contentRange = ""; // 響應(yīng)格式 // Content-Range: bytes [文件塊的開(kāi)始字節(jié)]-[文件的總大小 - 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 + ""); // 響應(yīng)的格式是: 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"); // 跳過(guò)已下載字節(jié) raf.seek(pastLength); if (isFlashGet) { // 快車(chē)等 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) { /** * 在寫(xiě)數(shù)據(jù)的時(shí)候 對(duì)于 ClientAbortException 之類(lèi)的異常 * 是因?yàn)榭蛻舳巳∠讼螺d,而服務(wù)器端繼續(xù)向?yàn)g覽器寫(xiě)入數(shù)據(jù)時(shí), 拋出這個(gè)異常,這個(gè)是正常的。 尤其是對(duì)于迅雷這種吸血的客戶端軟件。 * 明明已經(jīng)有一個(gè)線程在讀取 bytes=1275856879-1275877358, * 如果短時(shí)間內(nèi)沒(méi)有讀取完畢,迅雷會(huì)再啟第二個(gè)、第三個(gè)。。。線程來(lái)讀取相同的字節(jié)段, 直到有一個(gè)線程讀取完畢,迅雷會(huì) KILL * 掉其他正在下載同一字節(jié)段的線程, 強(qiáng)行中止字節(jié)讀出,造成服務(wù)器拋 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部分
<result name="failed" type="redirectAction">showDownloadFileNameList</result>
</action>
3. jsp部分
相關(guān)文章
關(guān)于Spring?Ioc和DI注解的問(wèn)題
這篇文章主要介紹了Spring?Ioc和DI注解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03Java實(shí)現(xiàn)的簡(jiǎn)單數(shù)字處理類(lèi)及用法示例
這篇文章主要介紹了Java實(shí)現(xiàn)的簡(jiǎn)單數(shù)字處理類(lèi)及用法,涉及java數(shù)字運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2018-01-01mybatis typeAliases 給實(shí)體類(lèi)起別名的方法
這篇文章主要介紹了mybatis typeAliases 給實(shí)體類(lèi)起別名,本文給大家分享兩種用法,通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09SpringBatch數(shù)據(jù)寫(xiě)入實(shí)現(xiàn)
Spring Batch通過(guò)ItemWriter接口及其豐富的實(shí)現(xiàn),提供了強(qiáng)大的數(shù)據(jù)寫(xiě)入能力,本文主要介紹了SpringBatch數(shù)據(jù)寫(xiě)入實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2025-04-04Filter、Servlet、Listener的學(xué)習(xí)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了Filter、Servlet、Listener的學(xué)習(xí)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Java并發(fā)編程學(xué)習(xí)之Unsafe類(lèi)與LockSupport類(lèi)源碼詳析
這篇文章主要給大家介紹了關(guān)于Java并發(fā)編程學(xué)習(xí)之Unsafe類(lèi)與LockSupport類(lèi)源碼的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧2018-06-06springboot大文件上傳、分片上傳、斷點(diǎn)續(xù)傳、秒傳的實(shí)現(xiàn)
本文主要介紹了springboot大文件上傳、分片上傳、斷點(diǎn)續(xù)傳、秒傳的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07