代码改变世界

多线程下载

2017-04-18 19:34  甘雨路  阅读(253)  评论(0编辑  收藏  举报
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  
    <servlet>
        <servlet-name>downloadImage</servlet-name>
        <servlet-class>mutidownload.ImageServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>downloadImage</servlet-name>
        <url-pattern>*.download</url-pattern>
    </servlet-mapping>
    
</web-app>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>图片下载</title>
  </head>
  
  <body>
    <div>
        <label>Tomcat</label><a href="http://localhost:8080/lf.download?url=http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-6/v6.0.53/bin/apache-tomcat-6.0.53.tar.gz">下载</a><br>
    </div>
  </body>
</html>
package mutidownload;

import java.io.IOException;
import java.nio.file.Path;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import mutidownload.util.DownUtil;

public class ImageServlet extends HttpServlet{
    
    
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        String path = req.getParameter("url");
        
        System.out.println(path);
        DownUtil downUtil =new DownUtil(path, "C:\\Users\\111\\Desktop\\test", 3);
        try {
            downUtil.download();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //转发
        req.getRequestDispatcher("/index.jsp").forward(req, resp);
    }
}
package mutidownload.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class DownUtil {
    /**
     * 被下载文件的路径
     */
    private String path;
    /**
     * 下载文件存放的目录
     */
    private String targetFilePath;  
    /**
     * 线程数量
     */
    private static int threadCount = 3;
    
    /**
     * 被下载文件的大小
     */
    private int connectionLength;
    
    /**
     * 定义下载的线程对象
     */
    private DownloadThread[] threads;
   
    /**
     * 构造方法 
     * @param path 要下载文件的网络路径
     * @param targetFilePath 保存下载文件的目录
     * @param threadCount 开启的线程数量
     */
    public DownUtil(String path, String targetFilePath, int threadCount) {
        this.path = path;
        this.targetFilePath = targetFilePath;
        this.threadCount = threadCount; 
    }

    /**
     * 下载文件
     */
    public void download() throws Exception{
        //连接资源
        URL url = new URL(path);
        // 打开连接
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        // 设置请求方式
        connection.setRequestMethod("GET");
        // 设置超时时间
        connection.setConnectTimeout(10000);
        // 获取响应状态码
        int code = connection.getResponseCode();
        if(code == 200){
            //获取资源大小
            connectionLength = connection.getContentLength();
            System.out.println(connectionLength);
            //在本地创建一个与资源同样大小的文件来占位
            RandomAccessFile randomAccessFile = new RandomAccessFile(new File(targetFilePath,getFileName(url)), "rw");
            randomAccessFile.setLength(connectionLength);
            /*
             * 将下载任务分配给每个线程
             */
            //计算每个线程理论上下载的数量
            int blockSize = connectionLength/threadCount;
            //为每个线程分配任务
            for(int threadId = 0; threadId < threadCount; threadId++){
                //线程开始下载的位置
                int startIndex = threadId * blockSize; 
                //线程结束下载的位置
                int endIndex = (threadId+1) * blockSize -1;
                //如果是最后一个线程,将剩下的文件全部交给这个线程完成
                if(threadId == (threadCount - 1)){  
                    endIndex = connectionLength - 1;
                }
                //开启线程下载
                new DownloadThread(threadId, startIndex, endIndex).start();
            }
              randomAccessFile.close();
        }

    }
    


    //下载的线程
    private class DownloadThread extends Thread{

        private int threadId;
        private int startIndex;
        private int endIndex;
         
        public DownloadThread(int threadId, int startIndex, int endIndex) {
            this.threadId = threadId;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }
        
        @Override
        public void run() {
            System.out.println("线程"+ threadId + "开始下载");
            try {
                //分段请求网络连接,分段将文件保存到本地.
                URL url = new URL(path);
                //加载下载位置的文件
                File downThreadFile = new File(targetFilePath,"downThread_" + threadId+".dt");
                RandomAccessFile downThreadStream = null;
                //如果文件存在
                if(downThreadFile.exists()){
                    downThreadStream = new RandomAccessFile(downThreadFile,"rwd");
                    String startIndex_str = downThreadStream.readLine();
                    this.startIndex = Integer.parseInt(startIndex_str);//设置下载起点

                }else{
                    downThreadStream = new RandomAccessFile(downThreadFile,"rwd");
                }
                // 打开连接
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(10000);

                //设置分段下载的头信息。  Range:做分段数据请求用的。格式: Range bytes=0-1024  或者 bytes:0-1024
                connection.setRequestProperty("Range", "bytes="+ startIndex + "-" + endIndex);

                System.out.println("线程_"+threadId + "的下载起点是 " + startIndex + "  下载终点是: " + endIndex);
                //200:请求全部资源成功, 206代表部分资源请求成功
                if(connection.getResponseCode() == 206){
                    //获取输入流
                    InputStream inputStream = connection.getInputStream();
                    //获取前面已创建的文件.
                    RandomAccessFile randomAccessFile = new RandomAccessFile(
                            new File(targetFilePath,getFileName(url)), "rw");
                    randomAccessFile.seek(startIndex);//文件写入的开始位置.


                    /*
                     * 将网络流中的文件写入本地
                     */
                    byte[] buffer = new byte[1024];
                    int length = -1;
                    //记录本次下载文件的大小
                    int total = 0;
                    while((length = inputStream.read(buffer)) > 0){
                        randomAccessFile.write(buffer, 0, length);
                        total += length;

                        /*
                         * 将当前现在到的位置保存到文件中
                         */
                        downThreadStream.seek(0);
                        downThreadStream.write((startIndex + total + "").getBytes("UTF-8"));
                    }

                    downThreadStream.close();
                    inputStream.close();
                    randomAccessFile.close(); 
                    //删除临时文件
                    cleanTemp(downThreadFile);
                    System.out.println("线程"+ threadId + "下载完毕");
                }else{
                    System.out.println("响应码是" +connection.getResponseCode() + ". 服务器不支持多线程下载");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    //删除线程产生的临时文件
    private synchronized void cleanTemp(File file){
        file.delete();
    }

    //获取下载文件的名称
    private String getFileName(URL url){
        String filename = url.getFile();
        return filename.substring(filename.lastIndexOf("/")+1);
    }

       
}