如何通过JAVA代码实现多线程分段下载+断点续传

多线程下载技术是很常见的一种下载方案,这种方式充分利用了多线程的优势,在同一时间段内通过多个线程发起下载请求,将需要下载的数据分割成多个部分,每一个线程只负责下载其中一个部分,然后将下载后的数据组装成完整的数据文件,这样便大大加快了下载效率。

原理很清楚,但是其中涉及到几个关键的问题:

  • 1.需要请求的数据如何分段。
  • 2.分段下载的数据如何组装成完整的数据文件。
  • 3.断点的数据如何保存,再次下载如何继续上次保存的下载进度继续下载。

话不多说,下面是JAVA代码实现

1.首先启动Nginx服务器(或者其他静态资源服务器都可以,主要为了方便资源下载)

public static HttpURLConnection http(String urls,String method){
		try {
			HttpURLConnection http = (HttpURLConnection) new URL(urls).openConnection();
			http.setRequestMethod(method);
//			http.setDoOutput(true);
//			http.setDoInput(true);
			http.setConnectTimeout(1000*60);
			http.setReadTimeout(1000*60);
//			http.setRequestProperty("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9");
//			http.setRequestProperty("Accept-Encoding","gzip, deflate, br");
//			http.setRequestProperty("Accept-Language","zh-CN,zh;q=0.9,ja;q=0.8,en;q=0.7");
//			http.setRequestProperty("Cache-Control","max-age=0");
			http.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36");
			return http;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	
	private static boolean isrange = false;
	private static long range = 0,all = 0;
	//private static ForkJoinPool pools = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
	
	public static void thread_download(JTextArea pane,Integer number,String... line){
		try {
			File desktop = FileSystemView.getFileSystemView().getHomeDirectory();
			File log_file = new File(desktop,"data.log");
			RandomAccessFile log = new RandomAccessFile(log_file,"rwd");
			if(log_file.length()>0){String url = log.readUTF();range = log.readLong();all = log.readLong();if(url!=null && range>0 && all>0){
				System.out.println("url "+url);
				System.out.println("range "+range);
				System.out.println("all "+all);
				line = new String[]{url};isrange = true;}}
			for (String urls : line) {
				String filename = urls.substring(urls.lastIndexOf("/")+1);
				HttpURLConnection http = http(urls,"GET");
				if(isrange)http.setRequestProperty("Range", "bytes=" + range + "-" + all);
				if(!String.valueOf(http.getResponseCode()).matches("(206|200)"))continue;
				long content_length = http.getContentLengthLong();
				File file = new File(desktop,filename);
				if(!file.exists()){
				RandomAccessFile raf = new RandomAccessFile(file,"rwd");
                raf.setLength(content_length);
                raf.close();}
                long blockSize = content_length / number;
                CompletableFuture<?>[] future = new CompletableFuture[number];
                AtomicLong array = new AtomicLong(0);
                CompletableFuture.runAsync(()->{
                	try {
						while(true){
							log.seek(0);
							log.writeUTF(urls);
							log.writeLong(isrange?array.get()+range:array.get());
							log.writeLong(isrange?all:content_length);
							long last_size = array.get();
							TimeUnit.SECONDS.sleep(1);
							long now = array.get(),length = now - last_size;
							long second = (content_length - now)/(length==0?1:length);
							String timer = String.format("%02d:%02d:%02d",second/3600,second%3600/60,second%60);
							boolean is_size;
							double size = (is_size = length>1024*1024)?length/1024d/1024d:length/1024d;
							String time = String.format("%.2f "+(is_size?"MB/S":"KB/S"),size);
							long now1 = isrange?now+range:now,length1 = isrange?all:content_length;
							pane.setText(String.format("名称:%s%n大小:%d/%dMB 进度:%d%% 速度:%s 时间:%s",
									filename,
									(int)((isrange?array.get()+range:array.get())/1024d/1024d),
									(int)((isrange?all:content_length)/1024d/1024d),
									(int)Math.ceil(100d*now1/length1),
									time,
									timer));
							if(array.get()>=content_length)break;
						}
						log.close();
						log_file.delete();
						isrange = false;
						range = all = 0;
						System.err.println("文件删除");
					} catch (Exception e) {
						e.printStackTrace();
					}
                });
                for (int threadId = 1; threadId <= number; threadId++) {
                	long index = threadId;
                	long startIndex = isrange?range+(threadId - 1) * blockSize:(threadId - 1) * blockSize;
                	 AtomicLong endIndex = new AtomicLong(startIndex + blockSize - 1);
                	 if(threadId == number)endIndex.set(isrange?all:content_length);
                	 System.err.println("线程" + threadId + "下载:" + startIndex + "字节~" + endIndex.get() + "字节");
                	 future[threadId - 1] = CompletableFuture.runAsync(()->{
						try{
							HttpURLConnection http2 = http(urls,"GET");
							http2.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex.get());
                    		if(http2.getResponseCode()!=206)return;
                    		InputStream is = http2.getInputStream();//返回资源
			                RandomAccessFile raf2 = new RandomAccessFile(file,"rwd");
			                raf2.seek(startIndex);
			                byte[] buffer = new byte[1024*200];
			                int len=0;
			                while((len = is.read(buffer))!=-1){raf2.write(buffer,0,len);array.set(array.get()+len); }
			                is.close();
			                raf2.close();
			                System.err.println("线程 "+index+" 完成");
						} catch (Exception e) {
							e.printStackTrace();
						}
                    });
                }
                CompletableFuture.allOf(future).join();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	private static Thread download_thread = null;
	private static volatile int down_status = 1;
	
	
	public static void main(String[] args) throws Exception {
		JDialog jdialog = new JDialog();
		jdialog.setTitle("文件下载");
		jdialog.setLayout(new FlowLayout(FlowLayout.CENTER,0,100));
		jdialog.setAlwaysOnTop(true);
		jdialog.setSize(600,500);
		jdialog.setResizable(false);
		jdialog.setLocationRelativeTo(null);
		jdialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
		//System.out.println("我是呵呵哈哈哈".replaceAll("(.)\\1+","$1"));
		JPopupMenu menu = new JPopupMenu("系统提示");
		JTextArea pane = new JTextArea();
		pane.setLineWrap(true);
		pane.setComponentPopupMenu(menu);
		pane.setFont(new Font("微软雅黑",Font.PLAIN,18));
		pane.setPreferredSize(new Dimension(550,300));
		ActionListener action = e->{
			JMenuItem menus = (JMenuItem)e.getSource();
			String text = pane.getSelectedText();
			switch (menus.getText()) {
			case "开始下载":
				System.out.println("text "+text);
				if(text == null || !text.matches("https?://(.+?)")){JOptionPane.showMessageDialog(jdialog,"URL地址错误!","系统提示",JOptionPane.INFORMATION_MESSAGE);break;
				}CompletableFuture.runAsync(()->thread_download(pane,4,text));
				break;
			case "继续下载":
				menus.setText("暂停下载");
				//if(pools.isShutdown())pools = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
				CompletableFuture.runAsync(()->thread_download(pane,4,text));
				break;
			case "暂停下载":
				menus.setText("继续下载");
				//if(!pools.isShutdown())pools.shutdownNow();
				break;
			}
		};
		Stream.of("开始下载","暂停下载","继续下载").forEach(a->{
			JMenuItem item = new JMenuItem(a);
			menu.add(item);
			item.setFont(new Font("黑体",Font.BOLD,18));
			item.addActionListener(action);
		});
		jdialog.add(pane);
		jdialog.setVisible(true);
		
		System.out.println("4444444444444444444444444444444444444444");
		//new Thread(()->JOptionPane.showMessageDialog(pane,pane,"系统提示!",JOptionPane.INFORMATION_MESSAGE)).start();
		new DropTarget(pane,DnDConstants.ACTION_COPY_OR_MOVE, new DropTargetAdapter()
	            {
	               @Override
	               public void drop(DropTargetDropEvent dtde)
	               {
							try {
								if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
								    dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
								    dtde.dropComplete(true);
								    @SuppressWarnings("unchecked")
								    List<File> list = (List<File>)dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
								    for (File file : list){
								    	System.out.println(file);
								    	if(!file.getName().endsWith(".txt"))continue;
								    	String[] line = Files.readAllLines(file.toPath(),StandardCharsets.UTF_8)
								    			.stream().filter(a->a.matches("https?://(.+?)")).toArray(String[]::new);
								    	if(line.length<1){JOptionPane.showMessageDialog(jdialog,"NO URLS","系统提示",JOptionPane.INFORMATION_MESSAGE);continue;}
								    	thread_download(pane,2,line);
								    }
								 }else dtde.rejectDrop();
							} catch (Exception e) {
								e.printStackTrace();
							}
	               }
	            });
		
		//BaseTools.text_lists("D:\\桌面\\test");
		//System.out.println("hj \"4545 455\" kl".replaceAll("(?<=\") (?<!\")","00"));
//		Matcher patter2 = Pattern.compile("[A-Z]{1}[a-z]+ (\\w+)",Pattern.DOTALL).matcher("Astring name");
//		while(patter2.find())System.out.println(patter2.group(1));
		//System.out.println(Arrays.toString(WebMvcConfig.class.getClasses()));
//		Files.copy(new URL("https://cdn.staticfile.org/vue/3.2.36/vue.global.min.js")
//				.openStream(),Paths.get("E:\\qwer\\qwer14\\NettyServer\\src\\main\\resources\\static\\vue.global.min.js"));
		
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是Java实现FTP大文件多线程分段下载代码示例: import java.io.*; import java.net.*; import java.util.concurrent.*; public class FTPDownload { private static final int NUM_THREADS = 10; public static void main(String[] args) { String ftpServer = "ftp.example.com"; String ftpFilePath = "/path/to/file"; String localFilePath = "/path/to/local/file"; ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS); try { URL url = new URL("ftp://" + ftpServer + ftpFilePath); URLConnection connection = url.openConnection(); long fileSize = connection.getContentLengthLong(); RandomAccessFile localFile = new RandomAccessFile(localFilePath, "rw"); localFile.setLength(fileSize); localFile.close(); long chunkSize = fileSize / NUM_THREADS; long remainder = fileSize % NUM_THREADS; CountDownLatch latch = new CountDownLatch(NUM_THREADS); for (int i = 0; i < NUM_THREADS; i++) { long startByte = i * chunkSize; long endByte = (i == NUM_THREADS - 1) ? fileSize - 1 : startByte + chunkSize - 1; executor.execute(new DownloadTask(ftpServer, ftpFilePath, localFilePath, startByte, endByte, latch)); } latch.await(); } catch (Exception e) { e.printStackTrace(); } finally { executor.shutdown(); } } private static class DownloadTask implements Runnable { private String ftpServer; private String ftpFilePath; private String localFilePath; private long startByte; private long endByte; private CountDownLatch latch; public DownloadTask(String ftpServer, String ftpFilePath, String localFilePath, long startByte, long endByte, CountDownLatch latch) { this.ftpServer = ftpServer; this.ftpFilePath = ftpFilePath; this.localFilePath = localFilePath; this.startByte = startByte; this.endByte = endByte; this.latch = latch; } public void run() { try { URL url = new URL("ftp://" + ftpServer + ftpFilePath); URLConnection connection = url.openConnection(); connection.setRequestProperty("Range", "bytes=" + startByte + "-" + endByte); InputStream input = connection.getInputStream(); RandomAccessFile localFile = new RandomAccessFile(localFilePath, "rw"); localFile.seek(startByte); byte[] buffer = new byte[1024]; int bytesRead = 0; while ((bytesRead = input.read(buffer)) != -1) { localFile.write(buffer, 0, bytesRead); } input.close(); localFile.close(); latch.countDown(); } catch (Exception e) { e.printStackTrace(); } } } } 笑话:为什么程序员总是说计算机只能做0和1的事情?因为他们从来都没有试过按下esc键!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值