Java多线程下载文件和断点下载 import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import javax.print.attribute.standard.Finishings; public class MultiDownload { static int ThreadCount = 3; //代表3个进程 static int finishedThread = 0; //记录文件下完的数字 //确定下载地址 static String path = "http://192.168.13.13:8080/QQPlayer.exe"; public static void main(String[] args) { //发送get请求,请求这个地址的资源 try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); if(conn.getResponseCode() == 200){ //拿到所请求资源文件的长度 int length = conn.getContentLength(); File file = new File("QQPlayer.exe"); //生成临时文件 RandomAccessFile raf = new RandomAccessFile(file, "rwd"); //设置临时文件的大小 raf.setLength(length); raf.close(); //计算出每个线程应该下载多少字节 int size = length / ThreadCount; for (int i = 0; i < ThreadCount; i++) { //计算线程下载的开始位置和结束位置 int startIndex = i * size; int endIndex = (i + 1) * size - 1; //如果是最后一个线程,那么结束位置写死 if(i == ThreadCount - 1){ endIndex = length - 1; } // System.out.println("线程" + i + "的下载区间是:" + startIndex + "---" + endIndex); new DownLoadThread(startIndex, endIndex, i).start(); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class DownLoadThread extends Thread{ int startIndex; int endIndex; int threadId; public DownLoadThread(int startIndex, int endIndex, int threadId) { super(); this.startIndex = startIndex; this.endIndex = endIndex; this.threadId = threadId; } @Override public void run() { //再次发送http请求,下载原文件 try { File progressFile = new File(threadId + ".txt"); //判断进度临时文件是否存在 if(progressFile.exists()){ FileInputStream fis = new FileInputStream(progressFile); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); //从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置 startIndex += Integer.parseInt(br.readLine()); fis.close(); } System.out.println("线程" + threadId + "的下载区间是:" + startIndex + "---" + endIndex); HttpURLConnection conn; URL url = new URL(MultiDownload.path); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); //设置本次http请求所请求的数据的区间 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); //请求部分数据,相应码是206 if(conn.getResponseCode() == 206){ //流里此时只有1/3原文件的数据 InputStream is = conn.getInputStream(); byte[] b = new byte[1024]; int len = 0; int total = 0; //拿到临时文件的输出流 File file = new File("QQPlayer.exe"); RandomAccessFile raf = new RandomAccessFile(file, "rwd"); //把文件的写入位置移动至startIndex raf.seek(startIndex); while((len = is.read(b)) != -1){ //每次读取流里数据之后,同步把数据写入临时文件 raf.write(b, 0, len); total += len; // System.out.println("线程" + threadId + "下载了" + total); //生成一个专门用来记录下载进度的临时文件 RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd"); //每次读取流里数据之后,同步把当前线程下载的总进度写入进度临时文件中 progressRaf.write((total + "").getBytes()); progressRaf.close(); } System.out.println("线程" + threadId + "下载完毕-------------------OK!"); raf.close(); //把记录进程的文件删除 必须3个进程全部都下载完成后才可以删除 MultiDownload.finishedThread++; synchronized (MultiDownload.path) { //有线程安全问题,静态变量为唯一的 if(MultiDownload.finishedThread == MultiDownload.ThreadCount){ for (int i = 0; i < MultiDownload.ThreadCount; i++) { File f = new File(i + ".txt"); f.delete(); } MultiDownload.finishedThread = 0; } } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }网络资源的单线程下载
package com.wsq.utils.download.demo01; import java.awt.Color; import java.awt.EventQueue; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.FileOutputStream; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JTextField; /** * @Author wsq * @Package com.wsq.utils.download * @Description: 网络资源的单线程下载 * @Date Created by wsq on 2017/12/31上午12:41. * @Modified By: */ @SuppressWarnings("serial") public class SingleThreadDownloadFrame extends JFrame { private JTextField tf_address; /** * Launch the application * * @param args */ public static void main(String args[]) { EventQueue.invokeLater(new Runnable() { public void run() { try { SingleThreadDownloadFrame frame = new SingleThreadDownloadFrame(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the frame */ public SingleThreadDownloadFrame() { super(); getContentPane().setLayout(null); setTitle("网络资源的单线程下载"); setBounds(100, 100, 500, 237); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JLabel label = new JLabel(); label.setText("网络资源的网址:"); label.setBounds(10, 88, 118, 18); getContentPane().add(label); tf_address = new JTextField(); tf_address.setBounds(117, 86, 357, 22); getContentPane().add(tf_address); final JButton button = new JButton(); button.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { String address = tf_address.getText().trim();// 获得网址 download(address); // 下载文件 } }); button.setText("单击开始下载"); button.setBounds(41, 144, 145, 28); getContentPane().add(button); final JButton button_1 = new JButton(); button_1.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { tf_address.setText(null);// 清除文本框内容 tf_address.requestFocus();// 文本框获得焦点 } }); button_1.setText("清 空"); button_1.setBounds(204, 144, 106, 28); getContentPane().add(button_1); final JButton button_2 = new JButton(); button_2.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { System.exit(0); } }); button_2.setText("退 出"); button_2.setBounds(328, 144, 106, 28); getContentPane().add(button_2); final JLabel label_1 = new JLabel(); label_1.setForeground(new Color(0, 0, 255)); label_1.setFont(new Font("", Font.BOLD, 24)); label_1.setText("网络资源的单线程下载"); label_1.setBounds(117, 21, 301, 48); getContentPane().add(label_1); } public void download(String urlAddr) { // 从指定网址下载文件 try { URL url = new URL(urlAddr); // 创建URL对象 URLConnection urlConn = url.openConnection(); // 获得连接对象 urlConn.connect(); // 打开到url引用资源的通信链接 InputStream in = urlConn.getInputStream(); // 获得输入流对象 String filePath = url.getFile(); // 获得完整路径 int pos = filePath.lastIndexOf("/"); // 获得路径中最后一个斜杠的位置 String fileName = filePath.substring(pos + 1); // 截取文件名 // FileOutputStream out = new FileOutputStream("C:/"+fileName); // 创建输出流对象 FileOutputStream out = new FileOutputStream("/Users/able/Desktop/" + fileName); // 创建输出流对象 byte[] bytes = new byte[1024]; // 声明存放下载内容的字节数组 int len = in.read(bytes); // 从输入流中读取内容 while (len != -1) { out.write(bytes, 0, len); // 将读取的内容写到输出流 len = in.read(bytes); // 继续从输入流中读取内容 } out.close(); // 关闭输出流 in.close(); // 关闭输入流 JOptionPane.showMessageDialog(null, "下载完毕"); } catch (Exception e) { e.printStackTrace(); } } }网络资源的单线程下载.png 网络资源的多线程下载(注意:如下代码是两个类)
类1: package com.wsq.utils.download.demo02; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.net.HttpURLConnection; import java.net.URL; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JTextField; /** * @Author wsq * @Package com.wsq.utils.download * @Description: 网络资源的多线程下载,主方法 * @Date Created by wsq on 2017/12/31上午12:41. * @Modified By: */ @SuppressWarnings("serial") public class MultiThreadDownFrame extends JFrame { private JTextField tf_address; /** * Launch the application * * @param args */ public static void main(String args[]) { EventQueue.invokeLater(new Runnable() { public void run() { try { MultiThreadDownFrame frame = new MultiThreadDownFrame(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } public MultiThreadDownFrame() { super(); getContentPane().setLayout(null); setTitle("网络资源的多线程下载"); setBounds(100, 100, 482, 189); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JButton button = new JButton(); button.setBounds(10, 95, 106, 28); getContentPane().add(button); button.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { try { String address = tf_address.getText();// 获得网络资源地址 download(address, "/Users/able/Desktop/apache-maven-3.5.2-bin.tar.gz", 100);// 调用download()方法,将下载的网络资源保存到磁盘 } catch (Exception e1) { e1.printStackTrace(); } } }); button.setText("下 载"); final JLabel label = new JLabel(); label.setText("网络资源的地址:"); label.setBounds(10, 44, 106, 18); getContentPane().add(label); tf_address = new JTextField(); tf_address.setBounds(114, 42, 341, 22); getContentPane().add(tf_address); final JButton button_1 = new JButton(); button_1.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { tf_address.setText(null); } }); button_1.setText("清 空"); button_1.setBounds(179, 95, 106, 28); getContentPane().add(button_1); final JButton button_2 = new JButton(); button_2.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { System.exit(0); } }); button_2.setText("退 出"); button_2.setBounds(349, 95, 106, 28); getContentPane().add(button_2); } public void download(String url, String dest, int threadNum) throws Exception { URL downURL = new URL(url);// 创建网络资源的URL HttpURLConnection conn = (HttpURLConnection) downURL.openConnection();// 打开网络边接 long fileLength = -1;// 用于存储文件长度的变量 int stateFlagCode = conn.getResponseCode();// 获得连接状态标记代码 if (stateFlagCode == 200) {// 网络连接正常 fileLength = conn.getContentLength();// 获得文件的长度 conn.disconnect();// 取消网络连接 } if (fileLength > 0) { System.out.println("下载启用的线程数threadNum:" + threadNum); long byteCounts = fileLength / threadNum + 1;// 计算每个线程的字节数 File file = new File(dest);// 创建目标文件的File对象 int i = 0; while (i < threadNum) { long startPosition = byteCounts * i;// 定义开始位置 long endPosition = byteCounts * (i + 1);// 定义结束位置 if (i == threadNum - 1) { DownMultiThread fileThread = new DownMultiThread(url, file, startPosition, 0);// 创建DownMultiThread线程的实例 new Thread(fileThread).start();// 启动线程对象 } else { DownMultiThread fileThread = new DownMultiThread(url, file, startPosition, endPosition);// 创建DownMultiThread线程的实例 new Thread(fileThread).start();// 启动线程对象 } i++; } JOptionPane.showMessageDialog(null, "完成网络资源的下载。"); } } } 类2: package com.wsq.utils.download.demo02; import java.io.BufferedInputStream; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import javax.swing.JOptionPane; /** * @Author wsq * @Package com.wsq.utils.download * @Description: 网络资源的多线程下载 * @Date Created by wsq on 2017/12/31上午12:41. * @Modified By: */ public class DownMultiThread implements Runnable { private String sUrl = "";// 网络资源地址 private File desFile;// 需要写入的目标文件对象 private long startPos;// 写入的开始位置 private long endPos;// 写入的结束位置 /** * @param sUrl 网络资源地址 * @param desFile 需要写入的目标文件对象 * @param startPos 写入的开始位置 * @param endPos 写入的结束位置 */ public DownMultiThread(String sUrl, File desFile, long startPos, long endPos) { this.sUrl = sUrl; this.desFile = desFile; this.startPos = startPos; this.endPos = endPos; } public void run() { try { URL url = new URL(sUrl);// 创建下载资源的URL对象 HttpURLConnection conn = (HttpURLConnection) url.openConnection();// 打开连接对象 conn.setRequestProperty("User-Agent", "NetFox");// 设置请求属性 String rangeProperty = "bytes=" + startPos + "-";// 定义范围属性 if (endPos > 0) { rangeProperty = "bytes=" + startPos + "-" + endPos;// 调整范围属性的值 } conn.setRequestProperty("RANGE", rangeProperty);// 指定范围属性 RandomAccessFile out = new RandomAccessFile(desFile, "rw");// 创建可读写的流对象 out.seek(startPos);// 指定读写的开始标记 InputStream in = conn.getInputStream();// 获得网络资源的输入流对象 BufferedInputStream bin = new BufferedInputStream(in);// 创建输入缓冲流对象 byte[] buff = new byte[2048];// 创建字节数组 int len = -1;// 声明存放读取字节数的变量 len = bin.read(buff);// 读取到内容并添加到字节数组 while (len != -1) { out.write(buff, 0, len);// 写入磁盘文件 len = bin.read(buff);// 读取到内容并添加到字节数组 } out.close();// 关闭流 bin.close();// 关闭流 conn.disconnect();// 断开连接 } catch (Exception ex) { JOptionPane.showMessageDialog(null, ex.getMessage()); } } }网络资源的多线程下载.png 下载网络资源的断点续传
package com.wsq.utils.download.demo03; import java.awt.BorderLayout; import java.awt.Color; import java.awt.EventQueue; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JTextField; /** * @Author wsq * @Package com.wsq.utils.download * @Description: 下载网络资源的断点续传 * @Date Created by wsq on 2017/12/31上午12:41. * @Modified By: */ @SuppressWarnings("serial") public class BreakPointSuperveneFrame extends JFrame { private JTextField tf_totalLength; private JTextField tf_residuaryLength; private JTextField tf_readToPos; private JTextField tf_address; private JTextField tf_endPos; private JTextField tf_startPos; private String urlAddress = "";// 用于存储网络资源的地址 private long totalLength = 0;// 存储网络资源的大小,以字节为单位 private long readToPos = 0;// 存储上次读取到的位置 private long residuaryLength = 0;// 存储未读内容的大小 /** * Launch the application * * @param args */ public static void main(String args[]) { EventQueue.invokeLater(new Runnable() { public void run() { try { BreakPointSuperveneFrame frame = new BreakPointSuperveneFrame(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the frame */ public BreakPointSuperveneFrame() { super(); getContentPane().setLayout(null); setTitle("下载网络资源的断点续传"); setBounds(100, 100, 514, 238); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); tf_startPos = new JTextField(); tf_startPos.setBounds(80, 165, 113, 22); getContentPane().add(tf_startPos); final JLabel label = new JLabel(); label.setText("起始位置:"); label.setBounds(10, 167, 74, 18); getContentPane().add(label); final JLabel label_1 = new JLabel(); label_1.setText("结束位置:"); label_1.setBounds(199, 167, 74, 18); getContentPane().add(label_1); tf_endPos = new JTextField(); tf_endPos.setBounds(267, 165, 117, 22); getContentPane().add(tf_endPos); final JLabel label_2 = new JLabel(); label_2.setText("网络资源的地址:"); label_2.setBounds(10, 52, 113, 18); getContentPane().add(label_2); tf_address = new JTextField(); tf_address.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { try { urlAddress = tf_address.getText().trim(); URL url = new URL(urlAddress);// 获得网络资源的URL HttpURLConnection connection = (HttpURLConnection) url.openConnection();// 获得连接对象 connection.connect();// 连接网络资源 totalLength = connection.getContentLength();// 获得网络资源的长度 connection.disconnect();// 断开连接 tf_totalLength.setText(String.valueOf(totalLength));// 显示总长度 tf_readToPos.setText("0");// 显示上次读取到的位置 residuaryLength = totalLength;// 未读内容为文件总长度 tf_residuaryLength.setText(String.valueOf(residuaryLength));// 显示未读内容 } catch (MalformedURLException e1) { e1.printStackTrace(); } catch (IOException e2) { e2.printStackTrace(); } } }); tf_address.setBounds(119, 50, 365, 22); getContentPane().add(tf_address); final JLabel label_3 = new JLabel(); label_3.setForeground(new Color(0, 0, 255)); label_3.setFont(new Font("", Font.BOLD, 14)); label_3.setText("输入网络资源的地址并回车,可以获得网络资源的大小。"); label_3.setBounds(10, 10, 384, 22); getContentPane().add(label_3); final JLabel label_4 = new JLabel(); label_4.setForeground(new Color(128, 0, 0)); label_4.setText("网络资源的大小为"); label_4.setBounds(10, 76, 113, 38); getContentPane().add(label_4); final JLabel label_5 = new JLabel(); label_5.setText("上次读取到"); label_5.setBounds(10, 123, 74, 18); getContentPane().add(label_5); tf_readToPos = new JTextField(); tf_readToPos.setBounds(80, 121, 113, 22); tf_readToPos.setEnabled(false); getContentPane().add(tf_readToPos); final JLabel label_6 = new JLabel(); label_6.setText("字节处,还剩"); label_6.setBounds(202, 123, 87, 18); getContentPane().add(label_6); tf_residuaryLength = new JTextField(); tf_residuaryLength.setBounds(285, 120, 117, 22); tf_residuaryLength.setEnabled(false); getContentPane().add(tf_residuaryLength); final JLabel label_7 = new JLabel(); label_7.setText("字节未读。"); label_7.setBounds(404, 123, 80, 18); getContentPane().add(label_7); final JLabel label_4_1 = new JLabel(); label_4_1.setForeground(new Color(128, 0, 0)); label_4_1.setText("个字节。"); label_4_1.setBounds(404, 76, 80, 38); getContentPane().add(label_4_1); tf_totalLength = new JTextField(); tf_totalLength.setBounds(119, 84, 283, 22); tf_totalLength.setEnabled(false); getContentPane().add(tf_totalLength); final JButton button = new JButton(); button.setBounds(395, 162, 89, 28); getContentPane().add(button); button.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { if (totalLength == 0) { JOptionPane.showMessageDialog(null, "没有网络资源。\n\n请输入正确的网址,然后回车。"); return; } long startPos = 0;// 起始位置 long endPos = 0;// 结束位置 try { startPos = Long.parseLong(tf_startPos.getText().trim());// 起始位置 endPos = Long.parseLong(tf_endPos.getText().trim());// 结束位置 } catch (Exception ex) { JOptionPane.showMessageDialog(null, "输入的起始位置或结束位置不正确。"); return; } readToPos = endPos;// 记录读取到的位置 residuaryLength = totalLength - readToPos;// 记录未读内容的大小 tf_readToPos.setText(String.valueOf(readToPos));// 显示读取到的位置 tf_residuaryLength.setText(String.valueOf(residuaryLength));// 显示未读字节数 tf_startPos.setText(String.valueOf(readToPos));// 设置下一个读取点的开始位置 tf_endPos.setText(String.valueOf(totalLength));// 设置下一个读取点的结束位置 tf_endPos.requestFocus();// 使结束位置文本框获得焦点 tf_endPos.selectAll();// 选择结束位置文本框中的全部内容,方便输入结束位置值 download(startPos, endPos);// 调用方法进行下载 } }); button.setText("开始下载"); } public void download(long startPosition, long endPosition) { try { URL url = new URL(urlAddress);// 获得网络资源的URL HttpURLConnection connection = (HttpURLConnection) url.openConnection();// 获得连接对象 connection.setRequestProperty("User-Agent", "NetFox");// 设置请求属性 String rangeProperty = "bytes=" + startPosition + "-";// 定义请求范围属性 if (endPosition > 0) { rangeProperty += endPosition;// 调整请求范围属性 } connection.setRequestProperty("RANGE", rangeProperty);// 设置请求范围属性 connection.connect();// 连接网络资源 InputStream in = connection.getInputStream();// 获得输入流对象 String file = url.getFile();// 获得文件对象 String name = file.substring(file.lastIndexOf('/') + 1);// 获得文件名 FileOutputStream out = new FileOutputStream("/Users/able/Desktop/" + name, true);// 创建输出流对象,保存下载的资源 byte[] buff = new byte[2048];// 创建字节数组 int len = 0;// 定义存储读取内容长度的变量 len = in.read(buff);// 读取内容 while (len != -1) { out.write(buff, 0, len);// 写入磁盘 len = in.read(buff);// 读取内容 } out.close();// 关闭流 in.close();// 关闭流 connection.disconnect();// 断开连接 if (readToPos > 0 && readToPos == totalLength) { JOptionPane.showMessageDialog(null, "完成网络资源的下载。\n单击“确定”按钮退出程序。"); System.exit(0); } } catch (Exception e) { e.printStackTrace(); } } }下载网络资源的断点续传.png