JavaSE多线程下载的实现

Posted Frank Q

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSE多线程下载的实现相关的知识,希望对你有一定的参考价值。

本文中主要提供的是java多线程下载文件方案,以及java多线程将临时进度保存到文件,多线程断点续传的实现;

1、多线程下载
2、将下载进度保存到临时文件,多线程的断定续传


1、多线程下载
本例中首先在Tomcat服务器中的WEBAPP/ROOT/文件夹下面放置了SoftwareOffer.exe的二进制可执行文件,如果放置图片的话,中间数据如果出错,不容易用肉眼识别,但是如果是二进制文件的话,如果中间任何二进制的一位数据出错,必然造成二进制可执行文件无法运行!所以测试选择二进制可执行文件比较妥当!
代码如下:


  • MultiDownloader.java
import java.net.HttpURLConnection;
import java.net.URL;
/**
 * 多线程下载器
 * @author YQ
 */
public class MultiDownloader 

    //总线程数
    private final static int totalThreadCount = 3;

    @SuppressWarnings("unused")
    public static void main(String[] args) throws Exception 
        System.out.println("多线程下载器");
        URL url = new URL("http://192.168.1.102:8080/SoftwareOffer.exe");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        int code = conn.getResponseCode();
        if(code == 200) 
            int length = conn.getContentLength();
            System.out.println("服务器资源文件长度:"+length);
            int blockSize = length / 3;//服务器资源等分为三份,每一份的大小相同
            System.out.println("资源文件每一份的到小:"+blockSize);

            for(int threadid = 0;threadid < totalThreadCount;threadid++) 
                int startPosition = threadid * blockSize;
                int endPosition = (threadid+1) * blockSize - 1;

                if(threadid == (totalThreadCount - 1)) 
                    endPosition = length - 1;
                
                System.out.println("线程:" + threadid + "下载范围:" + startPosition + "~" + endPosition);

                byte[] buffer = new byte[blockSize];
            

        
    


2、将下载进度保存到临时文件,多线程的断定续传
本例中同样在Tomcat服务器中的WEBAPP/ROOT/文件夹下面放置了SoftwareOffer.exe的二进制可执行文件;


  • DemoDownLoad.java
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * 将下载进度保存到临时文件
 * 多线程的断定续传
 * @author YQ
 *
 */

public class DemoDownLoad 
    static String path = "http://192.168.1.102:8080/SoftwareOffer.exe";
    static int threadCount = 3;
    static int finishedThread = 0;
    public static void main(String[] args) 
        //发送http请求,拿到目标文件的长度
        try 
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(8000);
            conn.setReadTimeout(8000);

            if(conn.getResponseCode() == 200) 
                //获取长度
                int length = conn.getContentLength();//算出进程的开始位置和结束位置

                //创建临时文件
                File file = new File(getNameFormPath(path));
                //临时文件的使用
                RandomAccessFile raf = new RandomAccessFile(file,"rwd");
                //设置临时文件的大小与目标文件相互一致
                raf.setLength(length);
                raf.close();
                //计算每一个线程下载的区间
                int size = length / threadCount;

                for (int id=0;id < threadCount;id++) 
                    int startIndex = id * size;
                    int endIndex = (id + 1) * size - 1;
                    if(id == threadCount - 1) 
                        endIndex = length - 1;
                    
                    System.out.println("线程" + id + "下载区间" + startIndex + "-" + endIndex);
                    new DownLoadThread(id, startIndex, endIndex).start();
                
            
         catch (MalformedURLException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
         catch (IOException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        
    

    public static String getNameFormPath(String path) 
        int index = path.lastIndexOf("/");
        return path.substring(index + 1);
    


class DownLoadThread extends Thread 

    int threadId;
    int startIndex;
    int endIndex;

    public DownLoadThread(int threadId, int startIndex, int endIndex) 
        super();
        this.threadId = threadId;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    

    @Override
    public void run() 
        //发送Http请求,三次下载则是需要三次下载请求
        try 
            File fileProgress = new File(threadId+".txt");
            int lastProgress = 0;
            if(fileProgress.exists()) 
                //读取进度临时文件中内容
                FileInputStream fis = new FileInputStream(fileProgress);
                BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                //得到上一次的下载进度数据
                lastProgress = Integer.parseInt(br.readLine());
                //改变下载开始位置,上一次下过的文件,这一次就不再进行请求
                startIndex += lastProgress;
                //进度临时文件的输入流必须关闭
                fis.close();
            

            URL url = new URL(DemoDownLoad.path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(8000);
            conn.setReadTimeout(8000);

            //设置请求数据的区间
            conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);

            //请求部分数据成功的响应码是206
            if(conn.getResponseCode() == 206) 
                InputStream is = conn.getInputStream();
                byte[] bytes = new byte[1024];
                int len = 0;
                File file = new File(DemoDownLoad.getNameFormPath(DemoDownLoad.path));
                RandomAccessFile raf = new RandomAccessFile(file, "rwd");
                //设置写入文件的开始位置
                raf.seek(startIndex);
                //当前线程下载的总进度
                int total = lastProgress;
                while((len = is.read(bytes)) != -1) 
                    raf.write(bytes,0,len);
                    total += len;
                    System.out.println("线程"+threadId+"下载了:"+total);
                    //创建一个文本临时文件,保存下载进度
                    RandomAccessFile rafProgress = new RandomAccessFile(fileProgress, "rwd");
                    //每次下载1024个字节,就马上把1024写入文本的临时文件
                    rafProgress.write((total+"").getBytes());
                    rafProgress.close();
                
                raf.close();
                System.out.println("线程"+threadId+"下载完成");

                DemoDownLoad.finishedThread++;
                //首先必须要保证三条线程全部都下载完毕才可以删除
                synchronized (DemoDownLoad.path) 
                    if(DemoDownLoad.finishedThread == 3) 
                        //可能会出现线程不安全
                        for (int i=0;i<DemoDownLoad.finishedThread;i++) 
                            File f = new File(i+".txt");
                            f.delete();
                        
                        DemoDownLoad.finishedThread = 0;
                    
                
            

         catch (MalformedURLException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
         catch (IOException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        
    

以上是关于JavaSE多线程下载的实现的主要内容,如果未能解决你的问题,请参考以下文章

[javaSE] 多线程通信(等待-唤醒机制)

JavaSE:线程同步机制

JAVASE02-Unit010: 多线程基础 TCP通信

JavaSE——多线程

JavaSE之多线程

javaSE复习之——线程