package com.slice;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Item;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import io.minio.*;
import io.minio.messages.Item;
import java.io.*;
import java.nio.file.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
public class MinIOFolderDownloader {
private final MinioClient minioClient;
private final ExecutorService executorService;
private final AtomicLong totalFiles = new AtomicLong(0);
private final AtomicLong completedFiles = new AtomicLong(0);
public MinIOFolderDownloader(String endpoint, String accessKey, String secretKey, int threadCount) {
this.minioClient = MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
this.executorService = Executors.newFixedThreadPool(threadCount);
}
public void downloadFolder(String bucketName, String folderPath, String localDir) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
Path localBasePath = Paths.get(localDir);
Files.createDirectories(localBasePath);
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder()
.bucket(bucketName)
.prefix(folderPath)
.recursive(true)
.build());
for (Result<Item> result : results) {
Item item = result.get();
if (!item.isDir()) {
totalFiles.incrementAndGet();
executorService.submit(() -> downloadFile(bucketName, item.objectName(), localBasePath));
}
}
printProgress();
}
private void downloadFile(String bucketName, String objectName, Path localBasePath) {
try {
Path localPath = localBasePath.resolve(objectName);
Files.createDirectories(localPath.getParent());
File localFile = localPath.toFile();
StatObjectResponse remoteStat = minioClient.statObject(
StatObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
// 检查文件是否已存在且完整
if (localFile.exists()) {
if (isFileComplete(localFile, remoteStat)) {
System.out.println("跳过已存在文件: " + objectName);
completedFiles.incrementAndGet();
return;
}
// 断点续传实现
if (localFile.length() < remoteStat.size()) {
System.out.printf("继续下载 %s (%d/%d bytes)%n",
objectName, localFile.length(), remoteStat.size());
resumeDownload(bucketName, objectName, localFile, remoteStat.size());
completedFiles.incrementAndGet();
return;
}
}
// 全新下载
System.out.println("开始下载: " + objectName);
minioClient.downloadObject(
DownloadObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.filename(localPath.toString())
.build());
completedFiles.incrementAndGet();
} catch (Exception e) {
System.err.println("下载失败 " + objectName + ": " + e.getMessage());
}
}
private boolean isFileComplete(File localFile, StatObjectResponse remoteStat) {
return localFile.length() == remoteStat.size() &&
localFile.lastModified() >= remoteStat.lastModified().toInstant().toEpochMilli();
}
private void resumeDownload(String bucketName, String objectName, File localFile, long remoteSize) throws Exception {
try (RandomAccessFile output = new RandomAccessFile(localFile, "rw");
InputStream stream = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.offset(localFile.length())
.build())) {
output.seek(localFile.length());
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = stream.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
}
}
private void printProgress() {
new Thread(() -> {
while (completedFiles.get() < totalFiles.get()) {
System.out.printf("\r进度: %d/%d (%.2f%%)",
completedFiles.get(),
totalFiles.get(),
(completedFiles.get() * 100.0 / totalFiles.get()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
System.out.println("\n下载完成!");
}).start();
}
public void shutdown() {
executorService.shutdown();
}
public static void main(String[] args) throws IOException {
MinIOFolderDownloader downloader = new MinIOFolderDownloader(
"url",
"testminio",
"testminio",
100);
try {
downloader.downloadFolder("slbqxcpdl", "Encast24hr-10min/", "D://qxminio/slbqxcpdl");
} catch (ServerException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
} finally {
downloader.shutdown();
}
}
}