断点续传主要是使用http协议中range的属性来取得资源的部分内容,由于一般服务是不对外直接提供url访问的,一般都是通过id,在servlet中输出byte[]来实现,所以要想实现断点续传一般要自己实现服务端和客户端,客户端保持文件的下载或上传状态,(保存在本地或者数据库中)。再进行中断时保持中断状态,在进行续传时,首先读出文件的状态,然后设置range属性信息发送续传请求。服务器收到续传请求,读取range属性值,从文件中读取数据,发送到客户端。上述是基本的原理,上传下载原理相同。
下面模拟两次从服务器中现在文件。
客户端:分多次取得部分文件内容,利用RandomAccessFile文件中读取数据,写到输出流中。
[html] view plain copy package org.nercita.zmx.servlet; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; public class LoadClient { public static void load(int start, int end) throws MalformedURLException, FileNotFoundException{ String endpoint = "http://localhost:8080/RestDemo/servlet/LoadServlet?id=5"; URL url = new URL(endpoint); try { HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestProperty("Content-Type","text/plain; charset=UTF-8"); conn.setRequestProperty("RANGE","bytes="+start+"-"+end); //header中增加range属性 conn.connect(); System.out.println(conn.getResponseCode()); System.out.println(conn.getContentLength()); System.out.println(conn.getContentType()); InputStream ins = (InputStream)conn.getContent(); String fileName=conn.getHeaderField("Content-Disposition"); fileName = new String(fileName.getBytes("ISO8859-1"), "UTF-8"); fileName=fileName.substring(fileName.lastIndexOf("\\")+1); System.out.println(fileName); RandomAccessFile raFile = new RandomAccessFile("E:\\"+fileName, "rw"); raFile.seek(start); byte[] buffer = new byte[4096]; int len = -1; while((len = ins.read(buffer))!=-1){ raFile.write(buffer,0,len); } raFile.close(); conn.disconnect(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { load(0,1000); load(1001,0); } }
服务端:获取分析range属性,从收入流读取内容字节流,利用RandomAccessFile输出到文件中。其中还解决了中文文件名乱码的问题,由于http协议中规定,当在网络中传输时,setHeader方法中的字符只能按ISO8859-1传输,所以这时候就要把Unicode字符转换成了ISO8859-1的编码传到客户端,客户端进行解码。否则会出现乱码。
[html] view plain copy package org.nercita.zmx.servlet; import java.io.IOException; import java.io.RandomAccessFile; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LoadServlet extends HttpServlet { private static final long serialVersionUID = 237208504975097723L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int id = Integer.parseInt(request.getParameter("id")); System.out.println(request.getContextPath()); String path = request.getServletContext().getRealPath("/load"); String filename = "" ; if (id == 1) filename = path+"JDK_API_1_5_zh_CN.CHM"; else if (id == 2) filename = path+"JDK_API_1_6_zh_CN.CHM"; else if (id == 3) filename = path+"tomcat.gif"; else filename = path+"\\断点续传text.txt"; RandomAccessFile raFile = new RandomAccessFile(filename, "r"); String range = request.getHeader("RANGE"); int start=0, end=0; if(null!=range && range.startsWith("bytes=")){ String[] values =range.split("=")[1].split("-"); start = Integer.parseInt(values[0]); end = Integer.parseInt(values[1]); } int requestSize=0; if(end!=0 && end > start){ requestSize = end - start + 1; response.addHeader("content-length", ""+(requestSize)); } else { requestSize = Integer.MAX_VALUE; } byte[] buffer = new byte[4096]; response.setContentType("application/x-download"); filename = new String(filename.getBytes("UTF-8"), "ISO8859-1"); response.addHeader("Content-Disposition", "attachment;filename="+filename); ServletOutputStream os = response.getOutputStream(); int needSize = requestSize; raFile.seek(start); while(needSize > 0){ int len = raFile.read(buffer); if(needSize < buffer.length){ os.write(buffer,0,needSize); } else { os.write(buffer,0,len); if(len < buffer.length){ break; } } needSize -= buffer.length; } raFile.close(); os.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }上述代码,已通过本人测试,有兴趣的可以一试!