文件多线程断点下载,附带进度显示

该博客介绍了如何实现一个支持多线程和断点续传的文件下载系统。服务器通过指定端口持续提供服务,当客户端连接时,服务器会发送文件名和大小信息。客户端接收到后创建对应文件,并根据服务器反馈的断点位置(默认为0)开始下载。通过3个类(Server, Server2, Client)的协作,实现了文件的多线程传输和进度显示功能。" 103107067,5837334,解决Dubbo jar包缺失:本地仓库部署与离线约束配置,"['Java', 'Dubbo', 'Maven', 'IDE配置', '依赖管理']

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

目标:模拟网络文件下载,准备一个服务器,一个客户端,服务器能够提供多用户同时文件下载,且若出现异常(断点,停止)下次能够接着之前的位置继续下载,服务端可显示进度

思路:服务器根据指定端口提供服务,当然作为服务器需要24小时不停运行,死循环是必须的,一旦有客户端连接进来,拿到对应的连接,然后对这个连接进行文件传输任务(多线程实现),首先将文件名和文件的大小发送给客户端,客户端收到后根据得到的文件名创建一个新文件。在文件开始传输之间需要先读取之前断开的位置,服务器这边的断点位置以客户端的为准,所以需要客户端发送断点位置,第一次传送是没有位置的,所以发送过来的是位置0,即从0开始,然后开始文件的传送。这个实现创建了3个类,Server(服务器),Server2(用来提供服务的),Client(客户端),具体代码如下:

public class Server{
	
	File file;
	
	public Server(String path){
		this.file = new File(path);
	}
	
	public void send(){
		try {
			ServerSocket ss = new ServerSocket(8888);
			// 服务器死循环不停执行
			while(true){
				// 等待连接
				Socket s = ss.accept();
				System.out.println("客户端"+s.getInetAddress().getHostAddress()+"已连接");
				// 对每一个连接上的用户多线程执行文件传输操作,
				new Server2(file,s).start();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Server ser = new Server("D:/test/1.mp4");
		ser.send();
	}
}
public class Server2 extends Thread {

	private File f;
	private Socket s;
	
	// 创建构造器用以接收Server中传过来的值
	public Server2(File f, Socket s) {
		super();
		this.f = f;
		this.s = s;
	}

	@Override
	public void run() {
		
		RandomAccessFile raf = null;
		BufferedOutputStream bos = null;
		PrintWriter pw = null;
		
		try {
			
			raf = new RandomAccessFile(f,"rw");
			
			// 获取基于Socket的输出流,发送文件名给客户端
			pw = new PrintWriter(s.getOutputStream());
			pw.println(f.getName()+"-"+f.length());
			pw.flush();

			// 获取从客户端发送过来的上一次断点位置
			BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
			long pos = Long.parseLong(br.readLine());
			// 将文件指针定位到上一次断点的位置
			raf.seek(pos);
			// 获取基于Socket的缓冲输出流用以向客户端发送文件
			bos = new BufferedOutputStream(s.getOutputStream());

			int len = 0;
			byte[] b = new byte[1024];
			while ((len = raf.read(b)) != -1) {
				bos.write(b, 0, len);
			}
			System.out.println("拷贝完成");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 资源回收
			try {
				if (bos != null) {
					bos.close();
				}
				if (raf != null) {
					raf.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
public class Client {
	
	File target;
	static File newFile;
	File positionFile;
	
	public Client(String dir){
		target = new File(dir);
	}
	
	// 根据服务端发过来的 文件名生成新文件并且得到文件大小
	public long createNewFile(Socket s) throws IOException, ClassNotFoundException{
		BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		// 因为服务端发送过来的字符串有指定格式,将其分割为文件名和文件大小,文件大小用来记录进度
		String[] str = br.readLine().split("-");
		newFile = new File(target,str[0]);
		return Long.parseLong(str[1]);
	}
	
	
	
	public void download() throws UnknownHostException, IOException, ClassNotFoundException{
		// 连接上服务器,指定ip和端口
		Socket s = new Socket("192.168.4.126",8888);
		// 创建新文件
		Long size = createNewFile(s);
		
		// 读取文件断点位置并发送给服务端
		PrintWriter pw = new PrintWriter(s.getOutputStream());
		positionFile = new File(target,s.getInetAddress().getHostAddress()+".txt");
		long pos = readPosition();
		pw.println(pos);;
		pw.flush();
		// 获得基于Socket的输入流和文件输出流,完成文件传输
		BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
		RandomAccessFile raf = new RandomAccessFile(newFile,"rw");
		// 同样需要定位上一次出现异常断点的位置
		raf.seek(pos);
		int len = 0;
		long current = pos;
		byte[] b = new byte[1024];
		while((len = bis.read(b)) != -1){
			raf.write(b, 0, len);
			current += len;
			// 显示进度(这里进度显示太过于频繁,可将其优化)
			showPro(current,size);
			// 每向文件写入一次,就将其位置写入记录文件中
			writePos(current);
		}
		System.out.println("下载完成");
		// 下载完成后,将记录文件删除
		positionFile.delete();
		
		raf.close();
		bis.close();
		pw.close();
	}
	// 用来显示进度(格式化的)
	private void showPro(long current, long size){
		double d = (double) current/size;
		String str = new DecimalFormat("##.#%").format(d);
		System.out.println("下载进度:"+ str);
	}
	// 写入断点位置
	private void writePos(Long pos) throws IOException {
		RandomAccessFile raf_pos = new RandomAccessFile(positionFile, "rw");
		raf_pos.writeLong(pos);
		raf_pos.close();
	}
	// 读取断点位置
	private long readPosition() throws IOException { 
		long pos = 0L;
		if(positionFile.exists()){
			System.out.println("继续下载...");
			RandomAccessFile raf_pos = new RandomAccessFile(positionFile, "r");
			pos = raf_pos.readLong();
			raf_pos.close();
		}else{
			System.out.println("开始下载...");
			positionFile.createNewFile();
		}
		return pos;
	}
	
	public static void main(String[] args) throws UnknownHostException, ClassNotFoundException, IOException{
		Client c = new Client("D:/test/test1");
		c.download();
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值