设计思路:
1. 首先读取文件的长度, 并判断网站是否支持分段下载
2. 如果支持分段下载则创建多个线程同时下载该文件,否则使用单线程下载
3. 在各个线程中,分别使用 RandomAccessFile对象写入对应的文件位置
package img.test; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; public class DownloadManager implements Runnable { // 保存路径 private String savePath; // 总的下载线程数 private int threadNum; // 下载的链接地址 private String urlFile; // 是否下载开始 private boolean isStarted; // 用于监视何时合并文件存放Thread的list private List<DownloadThread> downloadList = new ArrayList<DownloadThread>(); public DownloadManager(String savePath, int threadNum, String urlFile) { super(); this.savePath = savePath; this.threadNum = threadNum; this.urlFile = urlFile; } // 最终调用线程下载。本线程中调用分线程。 public void action() { new Thread(this).start(); } public void run() { long t1 = System.currentTimeMillis(); System.out.println(t1); // 如果没有下载 , 就开始 , 并且将已经下载的变量值设为true if (!isStarted) { startDownload(); isStarted = true; } while (true) { // 初始化认为所有线程下载完成,逐个检查 boolean finish = true; // 如果有任何一个没完成,说明下载没完成,不能合并文件 for (DownloadThread thread : downloadList) { if (!thread.isFinish()) { finish = false; break; } } // 全部下载完成才为真 if (finish) { // 合并文件 mergeFiles(); // 跳出循环 , 下载结束 break; } // 休息一会 , 减少cpu消耗 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } long t2 = System.currentTimeMillis(); System.out.println(t2); System.out.println("下载用时:" + (t2 -t1)); } public void startDownload() { // 得到每个线程开始值 , 下载字节数大小 int[][] posAndLength = getPosAndLength(); // 根据下载信息创建每个下载线程,并且启动他们。 for (int i = 0; i < posAndLength.length; i++) { int pos = posAndLength[i][0]; int length = posAndLength[i][1]; DownloadThread downloadThread = new DownloadThread(i + 1, length, pos, savePath, urlFile); new Thread(downloadThread).start(); downloadList.add(downloadThread); } } /** * 获得文件大小 * * @return 文件大小 */ public long getFileLength() { System.out.println("获得文件大小 start......"); HttpURLConnection conn = null; long result = 0; try { URL url = new URL(urlFile); conn = (HttpURLConnection) url.openConnection(); // 使用Content-Length头信息获得文件大小 result = Long.parseLong(conn.getHeaderField("Content-Length")); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); } } System.out.println("获得文件大小 end......" + result); return result; } // 具体细节求出每个线程的开始位置和文件下载大小 public int[][] getPosAndLength() { int[][] result = new int[threadNum][2]; int fileLength = (int) getFileLength(); int every = fileLength % threadNum == 0 ? fileLength / threadNum : fileLength / threadNum + 1; for (int i = 0; i < result.length; i++) { int length = 0; if (i != result.length - 1) { length = every; } else { length = fileLength - i * every; } result[i][0] = i * every; result[i][1] = length; } return result; } // 合并文件 public void mergeFiles() { System.out.println("合并文件 start......"); OutputStream out = null; try { out = new FileOutputStream(savePath); for (int i = 1; i <= threadNum; i++) { InputStream in = new FileInputStream(savePath + i); byte[] bytes = new byte[2048]; int read = 0; while ((read = in.read(bytes)) != -1) { out.write(bytes, 0, read); out.flush(); } if (in != null) { in.close(); new File(savePath + i).delete(); } } } catch (Exception e) { e.printStackTrace(); } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("合并文件 end......"); } public String getSavePath() { return savePath; } public void setSavePath(String savePath) { this.savePath = savePath; } public int getThreadNum() { return threadNum; } public void setThreadNum(int threadNum) { this.threadNum = threadNum; } public String getUrlFile() { return urlFile; } public void setUrlFile(String urlFile) { this.urlFile = urlFile; } public boolean isStarted() { return isStarted; } public void setStarted(boolean isStarted) { this.isStarted = isStarted; } public List<DownloadThread> getDownloadList() { return downloadList; } public void setDownloadList(List<DownloadThread> downloadList) { this.downloadList = downloadList; } }
package img.test; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; public class DownloadThread implements Runnable { // 当前第几个线程 , 用于给下载文件起名 file1 file2 file3 ... private int whichThread; // 监听单一线程下载是否完成 private boolean isFinish; // 本线程要下载的文件字节数 private int length; // 本线程向服务器发送请求时输入流的首位置 private int startPosition; // 保存的路径 private String savePath; // 要下载的文件 , 用于创建连接 private String url; public void run() { HttpURLConnection conn = null; InputStream in = null; OutputStream out = null; try { System.out.println("正在执行的线程:" + whichThread); URL fileUrl = new URL(url); // 与服务器创建连接 conn = (HttpURLConnection) fileUrl.openConnection(); // 下载使用get请求 conn.setRequestMethod("GET"); // 告诉服务器 , 我是火狐 , 不要不让我下载。 conn.setRequestProperty( "User-Agent", "Firefox Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3"); // 这里是设置文件输入流的首位置 conn.setRequestProperty("Range", "bytes=" + startPosition + "-"); // 与服务器创建连接 conn.connect(); // 获得输入流 in = conn.getInputStream(); // 在硬盘上创建file1 , file2 , ...这样的文件 , 准备往里面写东西 out = new FileOutputStream(savePath + whichThread); // 用于写入的字节数组 byte[] bytes = new byte[4096]; // 一共下载了多少字节 int count = 0; // 单次读取的字节数 int read = 0; while ((read = in.read(bytes)) != -1) { // 检查一下是不是下载到了本线程需要的长度 if (length - count < bytes.length) { // 比如说本线程还需要900字节,但是已经读取1000 // 字节,则用要本线程总下载长度减去 // 已经下载的长度 read = length - count; } // 将准确的字节写入输出流 out.write(bytes, 0, read); // 已经下载的字节数加上本次循环字节数 count = count + read; // 如果下载字节达到本线程所需要字节数,消除循环, // 停止下载 if (count == length) { break; } } // 将监视变量设置为true isFinish = true; } catch (Exception e) { e.printStackTrace(); } finally { // 最后进行输入、输出、连接的关闭 if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } if (conn != null) { conn.disconnect(); } } } public int getStartPosition() { return startPosition; } public void setStartPosition(int startPosition) { this.startPosition = startPosition; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public int getWhichThread() { return whichThread; } public void setWhichThread(int whichThread) { this.whichThread = whichThread; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } public String getSavePath() { return savePath; } public void setSavePath(String savePath) { this.savePath = savePath; } public DownloadThread(int whichThread, int length, int startPosition, String savePath, String url) { super(); this.whichThread = whichThread; this.length = length; this.startPosition = startPosition; this.savePath = savePath; this.url = url; } public DownloadThread() { super(); } public boolean isFinish() { return isFinish; } public void setFinish(boolean isFinish) { this.isFinish = isFinish; } }
package img.test; public class TestDownload { public static void main(String[] args) { DownloadManager downloadManager = new DownloadManager("f:/test.jpg" , 8 , ""); downloadManager.action(); } }
相关推荐
在这个场景中,"java多线程下载图片"意味着我们将探讨如何使用Java来实现一个能够异步下载多个图片的系统。 首先,我们需要理解Java中的线程是如何创建和运行的。Java提供了两种创建线程的方式:继承Thread类和实现...
在Java编程中,多线程是一项关键技能,尤其在处理并发任务时,如我们的示例——"Java多线程下载网络图片"。这个场景展示了如何利用多线程技术提高程序性能,减少用户等待时间,同时优化系统资源的使用。下面我们将...
为了提高程序的运行效率和响应速度,使用Java多线程来实现图片的下载与压缩成为了一个高效的解决方案。本文旨在详细探讨Java多线程在实现下载图片并进行压缩的具体实现方法,并提供了SpringMVC定时任务、FTP环境搭建...
Java多线程技术在爬虫应用中的重要性不言而喻,它能显著提升图片抓取的效率。本文将深入探讨如何使用Java实现多线程爬虫,以及压缩包中的三个示例案例。 首先,我们需要了解Java中的多线程概念。在Java中,通过创建...
在Java编程中,多线程下载大文件是一种常见的优化策略,尤其对于网络资源如文件或图片的下载。这种策略能够利用多核处理器的优势,通过并发处理来提高下载速度。本篇将详细介绍如何使用Java实现基于URL的单个大文件...
Java多线程学习是编程领域中的重要一环,特别是在服务器端和网络编程中,多线程技术能够有效地利用系统资源,提高程序的并发性。FTP(File Transfer Protocol)上传则是通过网络将本地文件传输到远程服务器的过程。...
本文将围绕“Java多线程实现坦克大战游戏带声音图片”这一主题,深入探讨如何利用Java的多线程技术来构建一个包含声音和图形的坦克战斗游戏。 首先,我们要理解Java中的线程。线程是程序执行的最小单元,每个线程都...
在Java编程语言中,实现多线程文件传输是一种优化程序性能、提高系统资源...在提供的`java多线程文件传输`压缩包中,可能包含了实现这些概念的示例代码,通过分析和学习,可以更好地理解多线程文件传输的原理和实践。
以下是一些关于使用Java多线程FTP上传和下载的关键知识点: 1. **FTPClient初始化**:创建`FTPClient`对象,并设置连接参数,如主机名、端口号、用户名和密码。使用`connect()`方法建立与FTP服务器的连接,然后调用...
总的来说,Java多线程技术在制作动画时起着至关重要的作用,它能帮助我们实现动态、流畅的视觉效果。通过巧妙地运用线程同步、定时器以及图形绘制API,可以构建出复杂且引人入胜的动画应用程序。在实际项目中,还...
首先,多线程下载文件是一种提高下载速度的方法,通过将大文件分成多个小部分,每个部分由一个单独的线程负责下载,从而充分利用多核处理器的并行处理能力。在Java中,我们可以使用`java.util.concurrent`包中的`...
在Android开发中,多线程异步下载图片是一项常见的任务,尤其在处理大量网络图片时,为了提升用户体验,我们通常会采用多线程技术来并发下载,同时利用内存缓存机制来减少网络请求,提高加载速度。本话题将深入探讨...
而`Java.jpg`可能是一个示例图片文件,用于演示多线程断点续传的实现。 总结来说,Java中的多线程断点续传结合了并发处理和状态管理,通过分割文件并行处理,提高了文件传输效率,同时通过记录和恢复断点,实现了在...
2. **多线程**:Java的`java.util.concurrent`包提供了线程池、Future、Callable等工具,用于管理多线程下载任务,确保资源的有效利用。 3. **文件操作**:下载的瓦片通常为图片格式,如JPEG或PNG,需要使用Java的`...
3. **图片下载**:当解析出图片URL后,为每个图片创建一个独立的线程进行下载。在下载过程中,需要考虑错误处理和异常情况,确保下载过程的健壮性。 4. **执行流程**:从指定的入口URL开始,不断循环处理`...
在Android开发中,多线程异步下载图片是一项常见的需求,尤其在开发涉及大量图片展示的应用时,如社交应用、电商应用等。这样的技术能够提高用户体验,避免UI卡顿,因为图片加载工作不会阻塞主线程。下面我们将深入...
在图片下载场景中,多线程下载能更快地将网页上的所有图片抓取到本地,尤其对于大型图片库或者网站,效率提升显著。 二、图片自动下载 图片自动下载工具通常具备以下功能: 1. 输入网址:用户只需要提供目标网站...
java多线程程序设计:Java NIO+多线程实现聊天室 Java基于多线程和NIO实现聊天室 涉及到的技术点 线程池ThreadPoolExecutor 阻塞队列BlockingQueue,生产者消费者模式 Selector Channel ByteBuffer ProtoStuff 高...
Java Socket多线程图片模拟视频是一种利用编程技术模拟视频播放的方法。在这个项目中,通过Java Socket通信和多线程技术,将连续的图片快速切换,从而达到类似视频流畅播放的效果。这种技术通常用于网络传输环境,...