Java多線程 之 多線程下載文件

*java

 文件結構:api

代碼以下:數組

1,網絡

package com.m0312.download.impl;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import com.m0312.download.api.Connection;

public class ConnectionImpl implements Connection{
    URLConnection urlCon;
    URL url;
    static final int BUFFER_SIZE = 1024;
    ConnectionImpl(String _url){
        try {
            url=new URL(_url);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
    @Override
    public byte[] read(int startPos, int endPos) throws IOException {
        //只能寫出第一部分
        byte[] buffer=new byte[endPos-startPos+1];
        HttpURLConnection urlCon2 = (HttpURLConnection)url.openConnection();
        urlCon2.setRequestProperty("Range", "bytes=" + startPos + "-"
                + endPos);
        InputStream is=urlCon2.getInputStream();
        //is.skip(startPos);
        is.read(buffer, 0, endPos-startPos+1);//由於沒有+1,一直是隻有三分之一部分
        is.close();
        return buffer;
        
        /**
         * 開始讀[0,1603]
            開始讀[1604,3207]
            is read length: 1024
            is read length: 1024
            baos.size: 1024
            baos.size: 1024
            開始讀[3208,4811]
            is read length: 580
            baos.size: 1604    ///size會累積,等於度過的全部buffer size
            is read length: 1024
            baos.size: 1024
            is read length: 580
            baos.size: 1604
            is read length: 580
            baos.size: 1604
         */
    }

    @Override
    public int getContentLength() {
        return urlCon.getContentLength();
    }

    @Override
    public void close() {
        if(urlCon!=null){
            //???
        }
    }
    @Override
    public URLConnection getUrlCon() {
        return urlCon;
    }
    @Override
    public void setUrlCon(URLConnection urlCon) {
        this.urlCon = urlCon;
    }

}

 

2,多線程

package com.m0312.download;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

import com.m0312.download.api.Connection;

public class DownloadThread extends Thread{

    Connection conn;
    int startPos;
    int endPos;
    String descFilePath;
    private CyclicBarrier cyclicBarrier;
    
    public DownloadThread( Connection conn, int startPos, int endPos){
        
        this.conn = conn;        
        this.startPos = startPos;
        this.endPos = endPos;
    }
    public DownloadThread( Connection conn, int startPos, int endPos,
            String descFilePath,CyclicBarrier cyclicBarrier){
        
        this.conn = conn;        
        this.startPos = startPos;
        this.endPos = endPos;
        this.descFilePath=descFilePath;
        this.cyclicBarrier=cyclicBarrier;
    }
    @Override
    public void run(){    
        try {
            /*byte[] bytes=conn.read(startPos, endPos);
            os=new FileOutputStream(new File(descFilePath)); 
            os.write(bytes, startPos, endPos-startPos+1);
            cyclicBarrier.await();//等待其餘線程
            */            
            System.out.println("開始讀["+startPos+","+endPos+"]");
            byte[] buffer = conn.read(startPos , endPos);
            RandomAccessFile file = new RandomAccessFile(descFilePath, "rw");
            file.seek(startPos);
            file.write(buffer, 0, buffer.length);
            file.close();
            cyclicBarrier.await();
            
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        //System.out.println("全部線程都下載完成");
        //通知 FileDownloader ,本身已經作完
        
    }
}

3,app

package com.m0312.download;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.concurrent.CyclicBarrier;

import com.m0312.download.api.Connection;
import com.m0312.download.api.ConnectionManager;
import com.m0312.download.api.DownloadListener;


public class FileDownloader {
    
    String url;
    
    DownloadListener listener;
    
    ConnectionManager cm;
    private static final int THREAD_NUM = 3;

    //定義幾個線程去下載  
    final int DOWN_THREAD_NUM = 3;  
    final String OUT_FILE_NAME = "e:/testfile/down.png";  
    InputStream[] isArr = new InputStream[DOWN_THREAD_NUM];  
    RandomAccessFile[] outArr = new RandomAccessFile[DOWN_THREAD_NUM];  
    
    public FileDownloader(String _url) {
        this.url = _url;
        
    }
    private void createPlaceHolderFile(String fileName, int contentLen) throws IOException{
        
        RandomAccessFile file = new RandomAccessFile(fileName,"rw");
        
        for(int i=0; i<contentLen ;i++){
            file.write(0);
        }
        
        file.close();
    }
    public void execute(){
        // 在這裏實現你的代碼, 注意: 須要用多線程實現下載
        // 這個類依賴於其餘幾個接口, 你須要寫這幾個接口的實現代碼
        // (1) ConnectionManager , 能夠打開一個鏈接,經過Connection能夠讀取其中的一段(用startPos, endPos來指定)
        // (2) DownloadListener, 因爲是多線程下載, 調用這個類的客戶端不知道何時結束,因此你須要實現當全部
        //     線程都執行完之後, 調用listener的notifiedFinished方法, 這樣客戶端就能收到通知。
        // 具體的實現思路:
        // 1. 須要調用ConnectionManager的open方法打開鏈接, 而後經過Connection.getContentLength方法得到文件的長度
        // 2. 至少啓動3個線程下載,  注意每一個線程須要先調用ConnectionManager的open方法
        // 而後調用read方法, read方法中有讀取文件的開始位置和結束位置的參數, 返回值是byte[]數組
        // 3. 把byte數組寫入到文件中
        // 4. 全部的線程都下載完成之後, 須要調用listener的notifiedFinished方法
        
        // 下面的代碼是示例代碼, 也就是說只有一個線程, 你須要改形成多線程的。
        Connection conn = null;
        try {
            
            conn = cm.open(this.url);
            
            int length = conn.getContentLength();
            File desc=new File(OUT_FILE_NAME);
            if(desc.exists()){
                desc.delete();
            }            
            createPlaceHolderFile(OUT_FILE_NAME, length);            
            CyclicBarrier barrier=new CyclicBarrier(THREAD_NUM,new Runnable() {
                @Override
                public void run() {
                    listener.notifyFinished();
                }
            });
            
            isArr[0] = conn.getUrlCon().getInputStream();
            int fileLen = conn.getContentLength();  
            System.out.println("網絡資源的大小" + fileLen);  
              
            // 每線程應該下載的字節數  
            long numPerThred = fileLen / DOWN_THREAD_NUM;  
            // 整個下載資源整除後剩下的餘數取模  
            long left = fileLen % DOWN_THREAD_NUM;  
            int start=0;
            int end=0;
            for (int i = 0; i < DOWN_THREAD_NUM; i++) {  
                
                // 爲每一個線程打開一個輸入流、一個RandomAccessFile對象,  
                // 讓每一個線程分別負責下載資源的不一樣部分。  
                //isArr[0]和outArr[0]已經使用,從不爲0開始  
                
                // 分別啓動多個線程來下載網絡資源  
                if (i == DOWN_THREAD_NUM - 1) {  
                    // 最後一個線程下載指定numPerThred+left個字節  
                    start=(int) (i * numPerThred);
                    end=(int) ((i + 1) * numPerThred  
                            + left-1);
                } else {  
                    // 每一個線程負責下載必定的numPerThred個字節  
                    start=(int) (i * numPerThred);
                    end=(int) ((i + 1) * numPerThred)-1;
                   
                } 
                new DownloadThread(conn, start, end,OUT_FILE_NAME,barrier).start();
                //Thread.sleep(1000);
            }  
            
        } catch (Exception e) {            
            e.printStackTrace();
        }finally{
            conn.close();
        }
    }
    
    public void setListener(DownloadListener listener) {
        this.listener = listener;
    }

    
    
    public void setConnectionManager(ConnectionManager ucm){
        this.cm = ucm;
    }
    
    public DownloadListener getListener(){
        return this.listener;
    }
    
}

4,dom

package com.m0312.download;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.m0312.download.api.ConnectionManager;
import com.m0312.download.api.DownloadListener;
import com.m0312.download.impl.ConnectionManagerImpl;

public class FileDownloaderTest {
    boolean downloadFinished = false;
    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void testDownload() {
        
        String url = "http://127.0.0.3:8082/applogo.png";
        
        FileDownloader downloader = new FileDownloader(url);

    
        ConnectionManager cm = new ConnectionManagerImpl();
        downloader.setConnectionManager(cm);
        
        downloader.setListener(new DownloadListener() {
            @Override
            public void notifyFinished() {
                downloadFinished = true;
            }
        });
        downloader.execute();
        
        // 等待多線程下載程序執行完畢
        while (!downloadFinished) {
            try {
                System.out.println("尚未下載完成,休眠五秒");
                //休眠5秒
                Thread.sleep(5000);
            } catch (InterruptedException e) {                
                e.printStackTrace();
            }
        }
        System.out.println("下載完成!");
        /**
         * 網絡資源的大小4812
            開始讀[0,1603]
            開始讀[1604,3207]
            開始讀[3208,4811]
            下載完成!
         */
        

    }

}

 

*ide

相關文章
相關標籤/搜索