断点续传/下载

在后台项目中经常会要求有下载和上传功能的实现,在大文件传输的过程中可以实现断点传输避免重复下载:现在我们来整理一下,也可以作为一个项目的亮点。

由于是本地测试,所以是将"D:/test/remote/file.txt"传送到"D:/test/local/file.txt",如果是使用FTP传送,使用FtpClient就可以了。

1 单线程读取

为方便整理逻辑,先使用单线程完成对需求的实现。
关键: RandomAccessFile 可以实现任意位置开始读取/写入

File f=new File(“D:/test.txt”);
RandomAccessFile localFile = new RandomAccessFile(f,“rw”);
localFile.seek(position);
localFile.write(buf);

code:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import com.springboot.SpringBootHello.service.FtpDownLoadService;
import com.springboot.SpringBootUtill.downLoadThread;

public class FtpDownLoadServiceImply  implements FtpDownLoadService{

	@Override
	public void downLoadSingleFile(File loacalFile, String remoteFilePath) {
		// TODO Auto-generated method stub
		File remoteFile=new File(remoteFilePath);
		checkFile(loacalFile);		//查看本地文件是否存在不存在则创建
		long localSize=loacalFile.length();
		long remoteSize=remoteFile.length();
		long step=localSize;//本地文件的大小作为标记的position;
		 if (localSize<remoteSize && localSize>=0) {
			continueDownLoad( loacalFile,  remoteFile,step);
		}
		else {
			System.err.println("本地文件已存在");
		}
		
		
	}

	@Override
	public void downLoadFiles() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void continueDownLoad(File loacalFile, File remoteFile,
			long startIndex) {
		// TODO Auto-generated method stub
		RandomAccessFile remoteFile2 = null;  // RandomAccessFile为任意位置起读取文件
		FileOutputStream localFile2 =null;
		try {
			 remoteFile2 =  new RandomAccessFile(remoteFile, "rw");
			 localFile2 = new FileOutputStream(loacalFile, true); // true表示在文末添加
			//RandomAccessFile localFile2 =  new RandomAccessFile(loacalFile, "rw");
			
			remoteFile2.seek(startIndex);//输入起始坐标
			//localFile2.seek(startIndex);
			// 数据缓冲区
			   byte[] buf = new byte[1];
			   while (remoteFile2.read(buf) != -1) {
				   localFile2.write(buf);
				   }
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally{
			//最后关闭IO流
			try {
				localFile2.close();
				remoteFile2.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}



	@Override
	public void checkFile(File filename) {
		// TODO Auto-generated method stub
		if (!filename.exists()) {
			try {
				filename.createNewFile();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}	
		
public static void main(String[] args) {
	FtpDownLoadServiceImply tt = new FtpDownLoadServiceImply();
	File localFile = new File("D:/test/local/file.txt");
	String remote = "D:/test/remote/file.txt";
	tt.downLoadSingleFile(localFile, remote);
}
}

2 多线程读取

上述代码已经实现了单线程的下载,我们只需要将单线程改为多线程就可以实现大文件分片下载。
关键:
将需要下载的部分进行划分 long partSize=(remoteSize-localSize)/threadNum;
设置每个线程所需要的标记位 long startIndex = localSize+partSize*(i-1);
long endIndex = localSize+partSize*i;
code:
下载方法:

	private void threadDownload(File  localFile,File remoteFile,   long localSize,long remoteSize) {
				int threadNum = 4;
				long partSize=(remoteSize-localSize)/threadNum;
				for (int i = 1; i <=threadNum; i++) {
					long startIndex = localSize+partSize*(i-1);
					long endIndex = localSize+partSize*i;
					if (i==threadNum) {
						endIndex=remoteSize;  //设置最后一个线程的终点位置
					}
					downLoadThread myThread = new downLoadThread(localFile,remoteFile,startIndex,endIndex,i);  
					Thread thread = new Thread(myThread);  
					//启动线程
					thread.start();					
				}
	}

线程类:


import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;


public class downLoadThread  implements  Runnable{
    private long startIndex;
    private long endIndex;
    private int i;
    private File remoteFile;
    private File loacalFile;
    
	public downLoadThread(File loacalFile, File remoteFile, long startIndex,
			long endIndex, int i ) {
		// TODO Auto-generated constructor stub
		 this.remoteFile = remoteFile;
		 this.loacalFile = loacalFile;
		 this.startIndex = startIndex;
		 this.endIndex = endIndex;
		 this.i = i;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		
		System.err.println("我是县城"+i+"     endIndex"+endIndex+"startIndex"+startIndex);
		RandomAccessFile remoteFile2 =null;
		RandomAccessFile localFile2 =null;
		try {
			
			remoteFile2 =  new RandomAccessFile(remoteFile, "rw");
			//FileOutputStream 也可以在文末写入
//			FileOutputStream localFile2 = new FileOutputStream(loacalFile, true);
			localFile2  =  new RandomAccessFile(loacalFile, "rw");
			remoteFile2.seek(startIndex);
			localFile2.seek(startIndex);
			int part= (int)(endIndex-startIndex);
			// 数据缓冲区
			   byte[] buf = new byte[1];
			   int hasRead=0;
			   int num= 0;
			   while ((hasRead=remoteFile2.read(buf)) != -1) {
				   num=hasRead+num;
				  
				   //线程终止,继续写入的话会重复写入
				   if (num>part) {
					break;
				}
				   localFile2.write(buf);
				   
				   }
			 
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally{
			  try {
				  remoteFile2.close();
				  localFile2.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}

我们来温习下多线程的实现方式
详情点击
1 继承 thread

public class myThread extends Thread{
	@override
	run{
		system.out.println("你好我是线程");
	}
}

启动线程

myThread  mt =  new myThread ();
mt.start();

2 实现接口Runnable

public class myThread implment Runnable{
	@override
	run{
		system.out.println("你好我是线程");
	}
}

启动线程

myThread  mt =  new myThread ();
Thread tt = new Thread (mt);
tt.start();

3 实现接口callable
4 使用线程池

猜你喜欢

转载自blog.csdn.net/qq_31941773/article/details/83106433