From 264df490e1ee0a98ad82a65c350d2002151eb7f4 Mon Sep 17 00:00:00 2001 From: johnChnia Date: Sat, 1 Apr 2017 00:36:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=9A=E7=BA=BF=E7=A8=8B=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../download/DownloadThread.java | 40 ++++++ .../download/FileDownloader.java | 123 ++++++++++++++++++ .../download/FileDownloaderTest.java | 61 +++++++++ .../download/api/Connection.java | 27 ++++ .../download/api/ConnectionException.java | 5 + .../download/api/ConnectionManager.java | 10 ++ .../download/api/DownloadListener.java | 5 + .../download/impl/ConnectionImpl.java | 71 ++++++++++ .../download/impl/ConnectionManagerImpl.java | 14 ++ 9 files changed, 356 insertions(+) create mode 100644 group24/315863321/src/main/java/com/johnChnia/coderising2017/download/DownloadThread.java create mode 100644 group24/315863321/src/main/java/com/johnChnia/coderising2017/download/FileDownloader.java create mode 100644 group24/315863321/src/main/java/com/johnChnia/coderising2017/download/FileDownloaderTest.java create mode 100644 group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/Connection.java create mode 100644 group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/ConnectionException.java create mode 100644 group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/ConnectionManager.java create mode 100644 group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/DownloadListener.java create mode 100644 group24/315863321/src/main/java/com/johnChnia/coderising2017/download/impl/ConnectionImpl.java create mode 100644 group24/315863321/src/main/java/com/johnChnia/coderising2017/download/impl/ConnectionManagerImpl.java diff --git a/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/DownloadThread.java b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/DownloadThread.java new file mode 100644 index 0000000000..e3dc19e8da --- /dev/null +++ b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/DownloadThread.java @@ -0,0 +1,40 @@ +package com.johnChnia.coderising2017.download; + +import com.johnChnia.coderising2017.download.api.Connection; + +import java.io.RandomAccessFile; +import java.util.concurrent.CyclicBarrier; + +public class DownloadThread extends Thread { + Connection conn; + int startPos; + int endPos; + CyclicBarrier barrier; + String localFile; + + public DownloadThread(Connection conn, int startPos, int endPos, String localFile, CyclicBarrier barrier) { + this.conn = conn; + this.startPos = startPos; + this.endPos = endPos; + this.localFile = localFile; + this.barrier = barrier; + } + + public void run() { + try { + System.out.println("Begin to read [" + startPos + "-" + endPos + "]"); + byte[] data = conn.read(startPos, endPos); + System.out.println("创建一个随机读取文件的对象"); + RandomAccessFile file = new RandomAccessFile(localFile, "rw"); + file.seek(startPos); + System.out.println("要写数据了"); + file.write(data); + file.close(); + conn.close(); + System.out.println(this.currentThread().getName() + "once over"); + barrier.await(); + } catch (Exception e) { + // TODO: handle exception + } + } +} diff --git a/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/FileDownloader.java b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/FileDownloader.java new file mode 100644 index 0000000000..02a0911bdd --- /dev/null +++ b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/FileDownloader.java @@ -0,0 +1,123 @@ +package com.johnChnia.coderising2017.download; + +import com.johnChnia.coderising2017.download.api.Connection; +import com.johnChnia.coderising2017.download.api.ConnectionManager; +import com.johnChnia.coderising2017.download.api.DownloadListener; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.concurrent.CyclicBarrier; + + +public class FileDownloader { + private String url; + private String localFile = "YNote.exe"; + DownloadListener listener; + ConnectionManager cm; + + private static final int DOWNLOAD_THREAD_NUM = 3; + + public FileDownloader(String _url) { + this.url = _url; +// this.localFile = localFile; + } + + 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 方法 + + // 下面的代码是实例代码,也就是说只有一个线程,你需要改造成多线程的 + CyclicBarrier barrier = new CyclicBarrier(DOWNLOAD_THREAD_NUM, new Runnable() {// 当所有的Thread都调用 await方法时,会执行后面的 barrierAction,调用后面这个线程 + @Override + public void run() { + listener.notifyFinished(); + } + }); + + Connection conn = null; + try { + conn = cm.open(this.url); + int length = conn.getContentLength();// 拿到将要下载文件的长度 + createPlaceHolderFile(this.localFile, length);//占位 + System.out.println("占位完毕"); + int[][] ranges = allocateDownloadRange(DOWNLOAD_THREAD_NUM, length);// 给每个线程分配开始位置和结束位置 + // 开始下载文件 + System.out.println("开始下载文件"); + for (int i = 0; i < DOWNLOAD_THREAD_NUM; i++) { + DownloadThread thread = new DownloadThread( + cm.open(url), + ranges[i][0], + ranges[i][1], + localFile, + barrier); + thread.start(); + System.out.println("第" + (i + 1) + "个线程已经启动"); + } + + } catch (Exception e) { + e.printStackTrace(); + } finally { + System.out.println("即将关闭连接"); + if (conn != null) { + conn.close(); + System.out.println("关闭连接成功"); + } + } + } + + public void setListener(DownloadListener listener) { + this.listener = listener; + } + + public void setConnectionManager(ConnectionManager ucm) { + this.cm = ucm; + } + + public DownloadListener getListener() { + return this.listener; + } + + private void createPlaceHolderFile(String fileName, int contentLen) throws IOException { + RandomAccessFile file = new RandomAccessFile(fileName, "rw"); + file.setLength(contentLen); +// for (int i = 0; i < contentLen; i++) { +// file.write(0); +// } + file.close(); + } + + /** + * 给出线程数和文件长度,返回一个二维数组,里面存的是每个线程下载的开始位置和结束位置 + * + * @param threadNum + * @param contentLen + * @return + */ + private int[][] allocateDownloadRange(int threadNum, int contentLen) { + int[][] ranges = new int[threadNum][2];// 用二维数组存下每个线程的开始位置和结束位置 + + int eachThreadSize = contentLen / threadNum;// 每个线程需要下载的文件大小 + int left = contentLen % threadNum;// 剩下的归最后一个线程来处理 + + for (int i = 0; i < threadNum; i++) { + int startPos = i * eachThreadSize; + int endPos = (i + 1) * eachThreadSize - 1; + if ((i == (threadNum - 1))) { + endPos += left; + } + ranges[i][0] = startPos; + ranges[i][1] = endPos; + } + + return ranges; + } + +} diff --git a/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/FileDownloaderTest.java b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/FileDownloaderTest.java new file mode 100644 index 0000000000..a831a51a3f --- /dev/null +++ b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/FileDownloaderTest.java @@ -0,0 +1,61 @@ +package com.johnChnia.coderising2017.download; + +import com.johnChnia.coderising2017.download.api.ConnectionManager; +import com.johnChnia.coderising2017.download.api.DownloadListener; +import com.johnChnia.coderising2017.download.impl.ConnectionManagerImpl; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.FileNotFoundException; + +public class FileDownloaderTest { + boolean downloadFinished = false; + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testDownload() throws FileNotFoundException { + + String url = "http://download.ydstatic.com/notewebsite/downloads/YNote.exe"; + + + 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("下载完成!"); + + + + } + +} diff --git a/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/Connection.java b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/Connection.java new file mode 100644 index 0000000000..9913d25a7f --- /dev/null +++ b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/Connection.java @@ -0,0 +1,27 @@ +package com.johnChnia.coderising2017.download.api; + +import java.io.IOException; + +public interface Connection { + /** + * 给定开始和结束位置, 读取数据, 返回值是字节数组 + * + * @param startPos 开始位置, 从0开始 + * @param endPos 结束位置 + * @return + */ + public byte[] read(int startPos, int endPos) throws IOException; + + + /** + * 得到数据内容的长度 + * + * @return + */ + public int getContentLength(); + + /** + * 关闭连接 + */ + public void close(); +} diff --git a/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/ConnectionException.java b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/ConnectionException.java new file mode 100644 index 0000000000..87c2121d4e --- /dev/null +++ b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/ConnectionException.java @@ -0,0 +1,5 @@ +package com.johnChnia.coderising2017.download.api; + +public class ConnectionException extends Exception { + +} diff --git a/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/ConnectionManager.java b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/ConnectionManager.java new file mode 100644 index 0000000000..0b904d1d7a --- /dev/null +++ b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/ConnectionManager.java @@ -0,0 +1,10 @@ +package com.johnChnia.coderising2017.download.api; + +public interface ConnectionManager { + /** + * 给定一个url , 打开一个连接 + * @param url + * @return + */ + public Connection open(String url) throws ConnectionException; +} diff --git a/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/DownloadListener.java b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/DownloadListener.java new file mode 100644 index 0000000000..6aa10e3bcc --- /dev/null +++ b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/api/DownloadListener.java @@ -0,0 +1,5 @@ +package com.johnChnia.coderising2017.download.api; + +public interface DownloadListener { + public void notifyFinished(); +} diff --git a/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/impl/ConnectionImpl.java b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/impl/ConnectionImpl.java new file mode 100644 index 0000000000..5d0095c5de --- /dev/null +++ b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/impl/ConnectionImpl.java @@ -0,0 +1,71 @@ +package com.johnChnia.coderising2017.download.impl; + +import com.johnChnia.coderising2017.download.api.Connection; +import com.johnChnia.coderising2017.download.api.ConnectionException; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; + +public class ConnectionImpl implements Connection { + URL url; + static final int BUFFER_SIZE = 1024; + + ConnectionImpl(String _url) throws ConnectionException { + try { + url = new URL(_url); + } catch (Exception e) { + throw new ConnectionException(); + } + } + @Override + public byte[] read(int startPos, int endPos) throws IOException { + //开始 + System.out.println("开始"); + HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); + // 设置读取的位置 + httpConn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos); + // 从URL连接获得输入流 + InputStream is = httpConn.getInputStream(); + + //is.skip(startPos); + byte[] buff = new byte[BUFFER_SIZE]; + int totalLen = endPos - startPos + 1; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + while(baos.size() < totalLen){ + int len = is.read(buff); + if(len<0){ + break; + } + baos.write(buff,0,len); + } + + if(baos.size() > totalLen){ + byte[] data = baos.toByteArray(); + return Arrays.copyOf(data, totalLen); + } + return baos.toByteArray(); + } + + @Override + public int getContentLength() { + URLConnection con; + try { + con = url.openConnection(); + return con.getContentLength(); + } catch (Exception e) { + e.printStackTrace(); + } + return -1; + } + + @Override + public void close() { + + } +} diff --git a/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/impl/ConnectionManagerImpl.java b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/impl/ConnectionManagerImpl.java new file mode 100644 index 0000000000..2c3a0cc520 --- /dev/null +++ b/group24/315863321/src/main/java/com/johnChnia/coderising2017/download/impl/ConnectionManagerImpl.java @@ -0,0 +1,14 @@ +package com.johnChnia.coderising2017.download.impl; + +import com.johnChnia.coderising2017.download.api.Connection; +import com.johnChnia.coderising2017.download.api.ConnectionException; +import com.johnChnia.coderising2017.download.api.ConnectionManager; + +public class ConnectionManagerImpl implements ConnectionManager { + + @Override + public Connection open(String url) throws ConnectionException { + return new ConnectionImpl(url); + } + +}