java实现浏览器多线程下载文件

it2025-04-08  20

浏览器请求头说明

HTTP 请求头 Range

请求资源的部分内容(不包括响应头的大小),单位是byte,即字节,从0开始. 如果服务器能够正常响应的话,服务器会返回 206 Partial Content 的状态码及说明. 如果不能处理这种Range的话,就会返回整个资源以及响应状态码为 200 OK . (这个要注意,要分段下载时,要先判断这个)

Range 请求头格式

Range: bytes=start-end Range: bytes=10- :第10个字节及最后个字节的数据 Range: bytes=40-100 :第40个字节到第100个字节之间的数据.

响应头说明

Content-Range: bytes 0-10/3103 //这个表示,服务器响应了前(0-10)个字节的数据,该资源一共有(3103)个字节大小。 Content-Type: image/png //表示这个资源的类型 Content-Length: 11 //表示这次服务器响应了11个字节的数据(0-10) Last-Modified: Tue, 30 Jun 2015 03:12:48 GMT //表示资源最近修改的时间(分段下载时要注意这个东西,因为如果修改了,分段下载可能就要重新下载了) ETag: W/"3103-1435633968000" //这个响应头表示资源版本的标识符,通常是消息摘要(类似MD5一样)

第一步 先获取要下载的文件大小

private static long getFileSize() { HttpURLConnection connection = null; HttpURLConnection conn = null; try { conn = (HttpURLConnection) new URL(url).openConnection(); conn.setConnectTimeout(3000); conn.setRequestMethod("HEAD"); conn.connect(); String headerField = conn.getHeaderField("Content-Disposition"); System.out.println("Content-Disposition:" + headerField); String eTag = conn.getHeaderField("ETag"); System.out.println("ETag:" + eTag); String contentType = conn.getContentType(); System.out.println("contentType:" + contentType); System.out.println("* 连接服务器成功"); } catch (MalformedURLException e) { throw new RuntimeException("URL错误"); } catch (IOException e) { System.err.println("x 连接服务器失败[" + e.getMessage() + "]"); return -1; } return conn.getContentLengthLong(); }

第二步根据线程计算每个线程要下载的数据

private static String url = "http://a.xzfile.com/down4/bitcometv1.71_downcc.com.zip"; //www.downcc.com 解压密码 private static int thnum=5; public static void main(String[] args) throws IOException { // File f = new File(getFileName()); boolean newFile = f.createNewFile(); RandomAccessFile rw = new RandomAccessFile(f.getName(), "rw"); FileChannel channel = rw.getChannel(); long fileSize = getFileSize(); //channel.write(ByteBuffer.allocate(0), 1); System.out.println("文件大小:"+fileSize); System.out.println(f.getAbsolutePath()); System.out.println(f.getCanonicalPath()); //f.renameTo(new File("D:\\java\\gsProject\\torrent\\com.zip"+"downloadTemp")); //ReName(f.getAbsolutePath(),"temp.zz"); int c = (int) (fileSize/thnum); for (int i = 0; i <thnum ; i++) { int begin = i*c; int end =begin+c; if(i == (thnum-1)){ end = (int) fileSize; } MyDownloadTask myDownloadTask = new MyDownloadTask(url,getFileName(),(int)fileSize,begin,end); Thread t = new Thread(myDownloadTask); t.start(); } // MyDownloadTask myDownloadTask = new MyDownloadTask(url,getFileName(),(int)fileSize); // myDownloadTask.run(); }

第三步每个线程下载自己要下载都数据

public class MyDownloadTask implements Runnable { //请求下载都url private String url; // private final RandomAccessFile file; //文件操作类 private final FileChannel channel; // 线程安全类 private int begin; private int end; private int pageSize; public MyDownloadTask(String url, String fileName, int pageSize,int begin ,int end) throws FileNotFoundException { this.url = url; this.file = new RandomAccessFile(fileName, "rw"); this.channel = file.getChannel(); this.pageSize = pageSize; this.begin=begin; this.end=end; } @SneakyThrows @Override public void run() { /* ReadableByteChannel connect = connect(); int byteread = 0; long lowerBound; System.out.println("强转数据"+pageSize); ByteBuffer buffer = ByteBuffer.allocate(pageSize); // 2MB System.out.println("开始下载-------------------"); //while ((byteread = connect.read(buffer)) != -1) { buffer.clear(); System.out.println("数据读了多长:"+connect.read(buffer)); System.out.println("下载中-- 当前文件大小:"+channel.size()); channel.write(buffer,0); System.out.println("下载中-- 当前文件大小:"+channel.size()); channel.close(); // } System.out.println("下载结束-------------------");*/ InputStream connect = connect(); byte [] bytes = new byte[1024]; int read ; ByteArrayOutputStream bos = new ByteArrayOutputStream(); while ((read=connect.read(bytes))!=-1){ bos.write(bytes,0,read); } ByteBuffer wrap = ByteBuffer.wrap(bos.toByteArray()); channel.write(wrap,begin); System.out.println(Thread.currentThread().getName()+":下载完成"); } /** * 连接WEB服务器,并返回一个数据通道 * * @return 返回通道 * @throws IOException 网络连接错误 */ private InputStream connect() throws IOException { HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); conn.setConnectTimeout(300000); conn.setRequestMethod("GET"); conn.setRequestProperty("Range", "bytes=" + begin+ "-"+end); conn.connect(); int statusCode = conn.getResponseCode(); if (HttpURLConnection.HTTP_PARTIAL != statusCode) { conn.disconnect(); throw new IOException("状态码错误:" + statusCode); } System.out.println(Thread.currentThread().getName()+"服务器响应字节:" + conn.getHeaderField("Content-Length")); /* InputStream inputStream = conn.getInputStream(); int i; int a = 0; while ((i = inputStream.read()) != -1) { a++; } System.out.println("返回字节数据:{}"+a);*/ // return Channels.newChannel(conn.getInputStream()); return conn.getInputStream(); } }
最新回复(0)