Java带进度多线程下载文件

本文介绍了一种利用多线程技术实现的大文件下载方案,并通过进度条实时展示下载进度。该方案分为三个部分:DownThread负责多线程下载任务;DownThreadClient用于测试和准备下载条件;ShowDownLoadPercentTask则负责显示下载进度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面讲了基本的IO,但是实际的项目中,文件大了之后一般会使用多线程来进行处理。本文主要是使用多线程对文件进行下载,并提供了进度显示。简单一点理解就是需要三个类,①一个类属于作业类,外界只需要告诉这个作业类输入流、输出流,当然本例主要讲多线程,那必然也需要传入待处理那段文件的起始位置,那么这个输出流必须使用RandomAccessFile来进行指定位置的写文件。②进度条处理类,由于进度条要实时更新,那么就需要使用java自带的Timer定时器,每隔1秒中读取所有线程的输入流中剩余的字节数,那么就能反算出此刻下载的进度。③作业和进度调用的测试类,这个类主要是分割文件,确认各个线程下载的起始位置,然后构建对应的输入输出流,再交给作业类来执行对应的任务。当任务分配完毕之后就可以启动定时器,每隔1秒去访问之前创建的输入流,进而确认下载进度。

主要包括三个类:DownThread、DownThreadClient、ShowDownLoadPercentTask。至于使用只需要将三个文件copy到自己的工作空间,DownThreadClient会有main函数来运行。

DownThread主要是控制多线程下载
DownThreadClient主要是测试下载任务、准备下载前的条件
ShowDownLoadPercentTask主要是显示下载进度

DownThread.java

package com.ds.io;

import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

public class DownThread extends Thread {

	private final int BUFF_LEN = 1024;
	private InputStream inputStream;
	private RandomAccessFile raf;
	private long start;
	private long end;
	private int flag = 1;
	
	/**
	 * @param start 下载开始位置
	 * @param end 下载结束位置
	 * @param inputStream 输入流
	 * @param raf 输出流
	 * @param flag 第n个线程
	 */
	public DownThread(long start,long end,InputStream inputStream,RandomAccessFile raf,int flag){
		this.start = start;
		this.end = end;
		this.inputStream = inputStream;
		this.raf = raf;
		this.flag = flag;
	}
	
	public void run(){
		//System.out.println("Thread "+ flag +" start!");
		try {
			//初始化输入输出流的位置
			inputStream.skip(start);
			raf.seek(start);
			byte[] buffer = new byte[BUFF_LEN];
			long contentLen = end - start;
			//设置读取界限,避免超过线程读取的文件分区范围
			int times = (int)(contentLen/BUFF_LEN);
			int hasRead = 0;
			//根据读取界限读取文件
			for(int i=0;i<=times;i++){
				hasRead = inputStream.read(buffer);
				if(hasRead == -1) break;
				if(i==times){
					raf.write(buffer, 0, (int)(contentLen%BUFF_LEN));
				}else {
					raf.write(buffer, 0, BUFF_LEN);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				//inputStream.close();暂时先别关闭输入流,计算下载进度时需要使用
				raf.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
DownThreadClient.java

package com.ds.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Timer;

import com.hundsun.jres.common.util.UUID;

public class DownThreadClient {
	
	//默认启动4个线程
	private int threadAccount = 4;
	private String fileSavePath = "C:/Users/Administrator/Desktop/";
	private InputStream[] inputStreams;
	private RandomAccessFile[] rdfs;
	private File file;

	public int getThreadAccount() {
		return threadAccount;
	}

	public InputStream[] getInputStreams() {
		return inputStreams;
	}

	public File getFile() {
		return file;
	}

	/**
	 * @param fileSavePath 新文件存储目录
	 * @param file 要下载的文件
	 */
	public DownThreadClient(String fileSavePath, File file) {
		this.fileSavePath = fileSavePath;
		this.file = file;
		inputStreams = new InputStream[threadAccount];
		rdfs = new RandomAccessFile[threadAccount];
	}

	/**
	 * 拼接出存储文件的绝对路径,文件名随机生成
	 * @param oldFileName 原始文件名
	 * @return
	 */
	public String getFilePath(String oldFileName){
		//获取原始文件名的后缀
		String suffix = oldFileName.substring(oldFileName.lastIndexOf("."));
		UUID uuid = UUID.randomUUID();
		String fileName = uuid.toString()+suffix;
		return fileSavePath+fileName;
	}
	
	//开始下载任务
	public void downLoad() throws Exception{
		long fileLen = file.length();//文件总长度
		long partLen = fileLen/threadAccount;//分区长度
		String newFilePath = getFilePath(file.getName());//文件存储新路径
		for(int i=0;i<threadAccount;i++){
			long start = i* partLen;
			long end = (i+1)*partLen;
			//初始化输入输出流
			inputStreams[i] = new FileInputStream(file);
			rdfs[i] = new RandomAccessFile(newFilePath, "rw");
			//如果是最后一段则设置下载结束位置为文件最末尾
			if(i==threadAccount-1){
				end = file.length();
			}
			//初始化并开启下载线程
			new DownThread(start, end, inputStreams[i], rdfs[i], i).start();
		}
	}
	
	/**
	 * 获取下载进度
	 * @param dtc DownThreadClient对象
	 */
	public void getDownLoadPercent(DownThreadClient dtc){
		Timer timer = new Timer();
		ShowDownLoadPercentTask sdlp = new ShowDownLoadPercentTask(dtc, timer);
		//延迟1秒开启任务,每秒钟执行一次
		timer.schedule(sdlp, 1000, 1000);
	}
	
	public static void main(String arg[]) throws Exception{
		String filePath = "E:/ds.rmvb";
		String fileSavePath = "C:/Users/Administrator/Desktop/";
		File file = new File(filePath);
		DownThreadClient dtc = new DownThreadClient(fileSavePath, file);
		dtc.downLoad();
		//显示下载进度
		dtc.getDownLoadPercent(dtc);
	}
}

ShowDownLoadPercentTask.java

package com.ds.io;

import java.io.IOException;
import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;

public class ShowDownLoadPercentTask extends TimerTask{

	private Timer timer;
	private DownThreadClient dtc;
	
	/**
	 * @param dtc DownThreadClient对象
	 * @param timer 定时器
	 */
	public ShowDownLoadPercentTask(DownThreadClient dtc, Timer timer) {
		super();
		this.dtc = dtc;
		this.timer = timer;
	}
	
	//关闭输入流
	public void closeIs(){
		try {
			for(int i=0; i<dtc.getThreadAccount(); i++){
				dtc.getInputStreams()[i].close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void run() {
		long currentLen = 0;
		long totleLen = dtc.getFile().length();
		try {
			//计算已读取的字节数
			for(int i=0; i<dtc.getThreadAccount(); i++){
				//计算方式:已读长度=总长度-可读长度-跳过长度
				currentLen += (totleLen - dtc.getInputStreams()[i].available()
						-i*(totleLen/dtc.getThreadAccount()));
			}
			//获取下载进度
			double percent = Math.ceil(currentLen*1.0/totleLen*10000);
			System.out.println(percent/100.0+"%");
			if(percent == 10000) {
				//停止定时任务,关闭输入流,删除备份文件
				timer.cancel();
				closeIs();
				System.out.println("下载完成");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值