服务器端代码:
package *;import java.io.*;import java.net.URLDecoder;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import biz.etao.appmarket.core.utils.StringUtil;/** * 客户端下载文件的统一入口 已支持断点续传 * * @author lianggj,charlie.deng */@WebServlet(value = "/download/*")public class FileDownloadServlet extends HttpServlet { private Logger logger = LoggerFactory.getLogger(FileDownloadServlet.class); /** * Comment for serialVersionUID
*/ private static final long serialVersionUID = 2822257745268393246L; public void init() throws ServletException { logger.info(FileDownloadServlet.class.getName() + " is inited."); } public void destroy() { super.destroy(); } /** * 下载文件 已支持断点续传 */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String base = "/download/"; String url = request.getRequestURI(); String path = url.substring(url.indexOf(base) + base.length()); path = URLDecoder.decode(path, "UTF-8"); // 获取文件对象信息 File file = new File(StringUtil.getFileTempReplace() + path); if (!file.exists()) { response.setStatus(404); return; } BufferedInputStream bis = null; try { String fileName = file.getName(); long fileLength = file.length(); long start = 0, end = fileLength - 1, contentLength = fileLength; String range = request.getHeader("Range"); if (range != null) { // client requests a file block download start byte String[] fromTo = range.split("=")[1].split("-"); start = Long.parseLong(fromTo[0]); if (fromTo.length == 2 && fromTo[1] != null && !fromTo[1].equals("")) end = Long.parseLong(fromTo[1]); if (end >= fileLength) end = fileLength - 1; } contentLength = end - start + 1; // support multi-threaded download // respone format: // Content-Length:[file size] - [client request start bytes from // file block] // tell the client to allow accept-ranges response.reset(); response.setHeader("Accept-Ranges", "bytes"); response.setContentLengthLong(contentLength); // String contentType = // Files.probeContentType(Paths.get(file.getPath())); String contentType = request.getSession().getServletContext().getMimeType(fileName); if (contentType == null && file.getPath().endsWith(".apk")) contentType = "application/vnd.android.package-archive"; response.setContentType(contentType); if (start > 0) { response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT); // 断点开始 // 响应的格式是: // Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小] String contentRange = new StringBuffer("bytes ").append(start).append("-").append(end).append("/") .append(contentLength).toString(); response.setHeader("Content-Range", contentRange); } response.addHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("utf-8"), "ISO8859-1")); // response.addHeader("Last-Modified:Mon", file.getvalue); // get file content byte[] buffer = new byte[4096]; int length = 0; long count = 0; OutputStream out = response.getOutputStream(); InputStream ins = new FileInputStream(file); bis = new BufferedInputStream(ins); if (start > 0) { // pointer move to seek bis.skip(start); } while ((length = bis.read(buffer)) != -1 && count < contentLength) { out.write(buffer, 0, length); count += length; } response.flushBuffer(); } catch (Exception ex) { String msg = this.getClass().getName() + ".doGet() error: " + ex.toString(); logger.error(msg); throw new ServletException(ex); } finally { if (bis != null) bis.close(); } }}
客服端代码:
public class TestDownload { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub HttpURLConnection httpURLConnection = null; URL url = null; BufferedInputStream bis = null; byte[] buf = new byte[10240]; int size = 0; String fileName = "aaa.zip"; String filePath = "C:\\Users\\Desktop"; String remoteUrl = "http://127.0.0.1:8080/down.zip"; // 检查本地文件 RandomAccessFile rndFile = null; File file = new File(filePath + "\\" + fileName); long remoteFileSize = getRemoteFileSzie(remoteUrl); long nPos = 0; if (file.exists()) { long localFileSzie = file.length(); if (localFileSzie < remoteFileSize) { System.out.println("文件续传..."); nPos = localFileSzie; } else { System.out.println("文件存在,重新下载..."); file.delete(); try { file.createNewFile(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } } else { // 建立文件 try { file.createNewFile(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } // 下载文件 try { url = new URL(remoteUrl); httpURLConnection = (HttpURLConnection)url.openConnection(); // 设置User-Agent httpURLConnection.setRequestProperty("User-Agent", "Net"); // 设置续传开始 httpURLConnection.setRequestProperty("Range", "bytes=" + nPos + "-"); // 获取输入流 bis = new BufferedInputStream(httpURLConnection.getInputStream()); rndFile = new RandomAccessFile(filePath + "\\" + fileName, "rw"); rndFile.seek(nPos); int i = 0; while ((size = bis.read(buf)) != -1) { //if (i > 500) break; rndFile.write(buf, 0, size); i++; } System.out.println("i=" + i); httpURLConnection.disconnect(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } public static long getRemoteFileSzie(String url) { long size = 0; try { HttpURLConnection httpUrl = (HttpURLConnection)(new URL(url)).openConnection(); size = httpUrl.getContentLength(); httpUrl.disconnect(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } return size; }}
参考: