package com.example.test.demo;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class 多线程下载单个文件 {
private static final int BUFFER_SIZE = 4096;
//线程数量
private static final int THREAD_COUNT = 20;
public static void main(String[] args) {
//源文件路径
//String fileUrl = "https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.11/bin/apache-tomcat-9.0.11.exe";
String fileUrl = "https://download5.mcloud.139.com/storageWeb/servlet/downloadServlet?code=TDAxNDExbDRoQVQzbnYzMjkxN3UybkFBZFdS&un=1A2CCFE331C8B0519052E6BA4AEDAF215DB052323B43DE4378B4E066AF941ABB&dom=D950&rate=0&txType=0";
//设置目标文件存放位置
String savePath = "E:\\下载\\Java\\" ;
//开始下载
multiThreadedDownloadOfFiles(fileUrl,savePath);
}
/**
* 多线程下载文件
* @param fileUrl 源文件url
* @param savePath 目标文件路径
*/
public static void multiThreadedDownloadOfFiles(String fileUrl,String savePath){
long startTime = System.currentTimeMillis();
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
try {
URL url = new URL(fileUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
String filename = connection.getHeaderField("Content-Disposition");
if (StringUtils.isEmpty(filename)){
String file = url.getFile();
//获取源文件名
filename = file.substring(file.lastIndexOf('/') + 1);
}else {
String[] split = filename.split("\"");
filename=split[1];
}
//设置目标文件路径
final String saveDir= savePath + filename;
//源文件大小
int fileSize = connection.getContentLength();
//文件分段名称
String partitionName = UUID.randomUUID().toString().replace("-", "");
for (int i = 0; i < THREAD_COUNT; i++) {
final int threadId = i;
int startByte = (fileSize / THREAD_COUNT) * threadId;
int endByte = (fileSize / THREAD_COUNT) * (threadId + 1) - 1;
if (threadId == THREAD_COUNT - 1) {
endByte = fileSize - 1;
}
final int endByte2 = endByte;
executorService.execute(() -> {
try {
//分段下载文件
downloadPartialFile(fileUrl, saveDir, startByte, endByte2, threadId, partitionName);
} catch (IOException e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
//等待所有线程执行完毕
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
//合并文件
mergePartialFiles(saveDir, THREAD_COUNT, partitionName);
long endTime = System.currentTimeMillis();
System.err.println("下载耗时:" + (endTime - startTime));
} catch (Exception e) {
e.printStackTrace();
executorService.shutdown();
}
}
/**
* 分段下载文件
*
* @param fileUrl 源文件路径
* @param saveDir 设置目标文件路径
* @param startByte 开始位置
* @param endByte 结束位置
* @param threadId 第几个分片
* @param partitionName 文件分段名称
* @throws IOException
*/
private static void downloadPartialFile(String fileUrl, String saveDir, int startByte, int endByte, int threadId, String partitionName) throws IOException {
// 创建URL对象并打开文件连接
URL url = new URL(fileUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置HTTP请求头,指定下载的字节范围
connection.setRequestProperty("Range", "bytes=" + startByte + "-" + endByte);
// 创建输入流并打开缓冲区
BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream());
// 创建文件输出流并将流定向到指定的保存目录
FileOutputStream fileOutputStream = new FileOutputStream(saveDir + partitionName + threadId + ".ext");
// 创建缓冲区
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
// 读取输入流并写入文件输出流,直到读取到文件末尾
while ((bytesRead = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, bytesRead);
}
// 关闭文件输出流和输入流
fileOutputStream.close();
inputStream.close();
// 打印已下载的文件范围
System.out.println("已下载部分文件: " + startByte + " - " + endByte);
}
/**
* 合并文件
*
* @param saveDir 设置目标文件路径
* @param partitionName 文件分段名称
* @throws IOException
*/
private static void mergePartialFiles(String saveDir, int threadCount, String partitionName) throws IOException {
// 新建一个随机访问文件输出流,指定文件路径和打开模式为可读写
RandomAccessFile outputFile = new RandomAccessFile(saveDir, "rw");
// 遍历线程数
for (int i = 0; i < threadCount; i++) {
// 拼接出分区文件名,指定文件路径、分区名和线程号
String partitionFileName = saveDir + partitionName + i + ".ext";
// 新建一个随机访问文件输入流,指定文件路径和打开模式为只读
RandomAccessFile partialFile = new RandomAccessFile(partitionFileName, "r");
// 定义一个缓冲区数组,指定缓冲区的大小
byte[] buffer = new byte[BUFFER_SIZE];
// 定义一个变量用于保存每次读取的字节数
int bytesRead;
// 循环读取每个分区文件的内容,直到到达文件末尾
while ((bytesRead = partialFile.read(buffer)) != -1) {
// 将读取的内容写入输出文件流
outputFile.write(buffer, 0, bytesRead);
}
// 关闭分区文件流
partialFile.close();
// 删除分区文件
File aa = new File(partitionFileName);
aa.delete();
}
// 关闭输出文件流
outputFile.close();
}
}
Java多线程下载单个文件
于 2023-11-15 10:16:20 首次发布