Dateien aus MinIO mit Java importieren
Laden Sie ein MinIO-Objekt in eine temporäre Datei herunter und stellen Sie es nach dem Lesen und Schließen der Antwort am gewünschten Ziel bereit. Dieser Java-Befehl lässt eine bestehende Zieldatei unverändert und endet mit Status 1, wenn der Import fehlschlägt. Eine lokale Demo lädt zuerst bekannte Binärbytes hoch, damit Sie prüfen können, was der Importer tatsächlich gespeichert hat.
Ihre Java-Umgebung einrichten
Diese Anleitung verwendet das MinIO Java SDK für einen selbst gehosteten Endpunkt und einen exakten Objektschlüssel. Informationen zu AWS-Zugangsdaten, Regionen und dem Verhalten des Transfer-Managers finden Sie in der separaten Java-Anleitung für Amazon S3.
Verwenden Sie Linux, Bash, OpenJDK 21.0.12.1 und Maven 3.9.16. Das Beispiel nutzt das
MinIO Java SDK 9.0.3. Maven lädt das SDK und die Build-Plugins von
Maven Central herunter. Setzen Sie JAVA_HOME auf Ihre JDK-Installation, wenn
Maven eine andere Java-Version verwendet.
Für die lokale Demo zur einmaligen Nutzung benötigen Sie außerdem cURL mit Unterstützung für
AWS Signature V4 sowie eine Server-Binärdatei von minio in
PATH. Bei der erneuten Ausführung wurden cURL 8.22.0 und MinIO verwendet,
wobei MinIO aus Revision 9e49d5e7a648 erstellt wurde.
MinIO Community Edition ist archiviert und wird nicht mehr gepflegt.
Diese Fixture dient dem lokalen Lernen und ist keine Empfehlung für einen neuen Produktiveinsatz.
Wenn Sie bereits einen MinIO-Dienst verwalten, können Sie den Importer stattdessen direkt damit
verwenden.
Wählen Sie ein übergeordnetes Verzeichnis mit Schreibzugriff. Fügen Sie dort diesen Block ein;
er erstellt ein neues Projekt und lässt Ihre Shell im übergeordneten Verzeichnis. Wenn
minio-import bereits existiert, stoppt er, bevor er darin schreibt.
(
set -eu
mkdir minio-import
cd minio-import
mkdir -p .mvn src/main/java
printf '<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"/>\n' > settings.xml
)
Speichern Sie die folgenden Dateien in minio-import. Das eigene Verzeichnis
.mvn verhindert, dass Maven die Konfiguration
.mvn eines übergeordneten Projekts verwendet. Das Startskript nutzt leere
Einstellungen und ein projektlokales Repository .m2, sodass Ihr
üblicher Maven-Cache für Abhängigkeiten nicht aktualisiert wird.
Das MinIO Java SDK integrieren
Speichern Sie diese vollständige Datei pom.xml. Compiler-Release und
Plugin-Versionen sind explizit angegeben; eine übergeordnete Parent-POM ist nicht erforderlich.
OkHttp 5 benötigt in Maven explizit sein JVM-Artefakt;
okhttp-jvm entspricht hier der Version, die das SDK verwendet.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>minio-import</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>9.0.3</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-jvm</artifactId>
<version>5.3.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.9.0</version>
</plugin>
</plugins>
</build>
</project>
Dateien aus MinIO importieren: Schritt für Schritt
Speichern Sie dies als src/main/java/MinIOFileImporter.java. Die Argumente sind Bucket, exakter
Objektschlüssel und lokales Ziel. Übergeben Sie den Schlüssel so, wie er gespeichert ist, etwa
reports/April report.bin; wandeln Sie ihn weder in eine URL um, noch wenden Sie selbst eine
Prozentkodierung darauf an.
import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.MinioClient;
import io.minio.errors.ErrorResponseException;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
public class MinIOFileImporter {
public static void main(String[] args) {
try {
long bytes = download(args);
System.out.println("Imported " + bytes + " bytes.");
} catch (FileAlreadyExistsException e) {
System.err.println("Destination exists; choose a new path.");
System.exit(1);
} catch (ErrorResponseException e) {
System.err.println("Storage refused the download (HTTP " + e.response().code() + ").");
System.exit(1);
} catch (Exception e) {
System.err.println("Import failed (" + e.getClass().getSimpleName() + "). Check configuration, network, and disk.");
System.exit(1);
}
}
private static String required(String name) {
String value = System.getenv(name);
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("Missing " + name);
}
return value;
}
private static long download(String[] args) throws Exception {
if (args.length != 3 || args[0].isBlank() || args[1].isEmpty() || args[2].isBlank()) {
throw new IllegalArgumentException("Expected bucket, object key, and destination");
}
String endpoint = required("MINIO_ENDPOINT");
String region = required("MINIO_REGION");
String accessKey = required("MINIO_ACCESS_KEY");
String secretKey = required("MINIO_SECRET_KEY");
Path destination = Path.of(args[2]).toAbsolutePath();
if (Files.exists(destination, LinkOption.NOFOLLOW_LINKS)) {
throw new FileAlreadyExistsException(destination.toString());
}
if (!Files.isDirectory(destination.getParent())) {
throw new IOException("Destination parent must already exist");
}
Path staged = Files.createTempFile(destination.getParent(), ".minio-", ".part");
try {
long copied;
try (MinioClient client = MinioClient.builder()
.endpoint(endpoint).region(region).credentials(accessKey, secretKey).build()) {
client.setTimeout(10_000, 30_000, 30_000);
try (GetObjectResponse response = client.getObject(
GetObjectArgs.builder().bucket(args[0]).object(args[1]).build())) {
String length = response.headers().get("Content-Length");
if (length == null) throw new IOException("Missing Content-Length");
long expected = Long.parseLong(length);
copied = Files.copy(response, staged, StandardCopyOption.REPLACE_EXISTING);
if (expected < 0 || copied != expected) {
throw new IOException("Incomplete response body");
}
}
}
Files.move(staged, destination);
return copied;
} finally {
Files.deleteIfExists(staged);
}
}
}
Der Vertrag für getObject im SDK verlangt,
dass die zurückgegebene Antwort geschlossen wird. Hier werden sowohl Antwort als auch Client vor
der Bereitstellung geschlossen. Ein leeres Objekt ist gültig und erzeugt eine Datei mit null
Bytes. Der Längenvergleich erkennt einen unvollständigen Antwortkörper; er ist weder eine
unabhängige Prüfsumme noch ein Beleg dafür, dass der Server den beabsichtigten Inhalt gespeichert
hat.
Speichern Sie download.sh im Projektstamm. Führen Sie das Skript immer aus diesem
Verzeichnis aus. Es kompiliert die bereitgestellte Klasse und kopiert die Laufzeitabhängigkeiten,
bevor es Java startet. Bei einem fehlgeschlagenen Build stoppt die Shell, selbst wenn eine ältere
kompilierte Klasse vorhanden ist.
#!/usr/bin/env bash
set -eu
if [ ! -d .mvn ] || [ ! -f settings.xml ] || [ ! -f src/main/java/MinIOFileImporter.java ]; then
printf 'Run download.sh from the minio-import project root.\n' >&2
exit 1
fi
if [ "$#" -ne 3 ]; then
printf 'Usage: bash download.sh BUCKET OBJECT_KEY DESTINATION\n' >&2
exit 1
fi
MAVEN_SKIP_RC=1 MAVEN_ARGS= MAVEN_OPTS= MAVEN_BASEDIR="$PWD" \
mvn --batch-mode --no-transfer-progress --settings settings.xml \
--global-settings settings.xml -Dmaven.repo.local="$PWD/.m2" \
compile dependency:copy-dependencies -DincludeScope=runtime
java -cp 'target/classes:target/dependency/*' MinIOFileImporter "$@"
Verwenden Sie ein lokales Verzeichnis, das Sie kontrollieren, und führen Sie pro Ziel einen
Importer aus. Beim Verschieben wird REPLACE_EXISTING weggelassen, entsprechend der
Verschieberichtlinie von Java. Diese sequenzielle Anleitung
verspricht weder gleichzeitige Bereitstellung noch Beständigkeit bei Abstürzen. Bei behandelten
Transferfehlern werden temporäre Staging-Dateien entfernt und keine Zieldatei hinterlassen. Eine
zwangsweise beendete JVM kann eine Datei .minio-*.part hinterlassen; entfernen Sie
sie erst, nachdem Sie sich vergewissert haben, dass der Importer gestoppt ist. Ein Dateisystemfehler
bei der Bereitstellung oder Bereinigung kann es erforderlich machen, das Verzeichnis vor einem
erneuten Versuch zu prüfen.
Ein lokales Objekt vom Upload bis zum Download verifizieren
Speichern Sie demo.sh im Projektstamm. Das Skript startet einen privaten
Loopback-Server mit Zugangsdaten zur einmaligen Nutzung, lädt eine binäre Fixture mit acht Bytes
per cURL hoch und ruft download.sh auf. Es bewahrt die Fixture, das Serverlog,
die Serverdaten und die heruntergeladene Datei zur Prüfung auf und stoppt seinen Server beim
Beenden. Es kontaktiert keinen öffentlichen Demo-Endpunkt.
#!/usr/bin/env bash
set -eu
port=${1:-19000}
case "$port" in ''|*[!0-9]*) printf 'Supply a numeric local port.\n' >&2; exit 1;; esac
if [ "$port" -lt 1024 ] || [ "$port" -gt 65535 ]; then
printf 'Use a local port from 1024 through 65535.\n' >&2
exit 1
fi
if [ ! -f download.sh ] || [ ! -d .mvn ]; then
printf 'Run demo.sh from the minio-import project root.\n' >&2
exit 1
fi
command -v minio >/dev/null
command -v curl >/dev/null
mkdir demo-data
mkdir demo-certs
export MINIO_ENDPOINT="http://127.0.0.1:$port" MINIO_REGION=us-east-1
export MINIO_ACCESS_KEY=local-demo MINIO_SECRET_KEY=local-demo-secret
MINIO_ROOT_USER="$MINIO_ACCESS_KEY" MINIO_ROOT_PASSWORD="$MINIO_SECRET_KEY" \
MINIO_BROWSER=off minio server demo-data --certs-dir demo-certs \
--address "127.0.0.1:$port" \
> demo-server.log 2>&1 &
server_pid=$!
stop_server() {
kill "$server_pid" 2>/dev/null || :
wait "$server_pid" 2>/dev/null || :
}
trap stop_server EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
ready=0
for attempt in {1..100}; do
if ! kill -0 "$server_pid" 2>/dev/null; then
printf 'MinIO startup failed; inspect demo-server.log.\n' >&2
exit 1
fi
if grep -q '^API:' demo-server.log && \
curl -fsS --max-time 1 "$MINIO_ENDPOINT/minio/health/ready" >/dev/null 2>&1; then
ready=1
break
fi
sleep 0.1
done
if [ "$ready" -ne 1 ]; then
printf 'MinIO did not become ready; inspect demo-server.log.\n' >&2
exit 1
fi
printf '\000\377MinIO\n' > sample.bin
curl -fsS --max-time 10 --aws-sigv4 'aws:amz:us-east-1:s3' \
--user "$MINIO_ACCESS_KEY:$MINIO_SECRET_KEY" -X PUT "$MINIO_ENDPOINT/demo-bucket"
curl -fsS --max-time 10 --aws-sigv4 'aws:amz:us-east-1:s3' \
--user "$MINIO_ACCESS_KEY:$MINIO_SECRET_KEY" --upload-file sample.bin \
"$MINIO_ENDPOINT/demo-bucket/fixtures/sample.bin"
bash download.sh demo-bucket 'fixtures/sample.bin' ./downloaded.bin
cmp sample.bin downloaded.bin
printf 'Verified downloaded.bin against sample.bin.\n'
Die cURL-Option --aws-sigv4 signiert die Anfragen
zum Erstellen des Buckets und zum Hochladen der Fixture unabhängig vom Java-Downloader. Wählen Sie
einen ungenutzten lokalen Port und fügen Sie Folgendes im übergeordneten Verzeichnis ein:
(cd minio-import && bash demo.sh 19000)
Nach der Build-Ausgabe von Maven sollten diese Zeilen und der Exit-Status 0 erscheinen:
Imported 8 bytes.
Verified downloaded.bin against sample.bin.
Eine Wiederholung der vollständigen Demo stoppt bei mkdir demo-data und gibt einen
Fehler zurück, wobei die bisherigen Dateien erhalten bleiben. Verwenden Sie für eine weitere
vollständige Demo ein neues Projektverzeichnis. Auch der Importer selbst lehnt eine vorhandene
Datei downloaded.bin ab; er entfernt sie niemals, um einen erneuten Versuch zu
ermöglichen.
Ihren bestehenden MinIO-Endpunkt verwenden
Stellen Sie MINIO_ENDPOINT, MINIO_REGION,
MINIO_ACCESS_KEY und MINIO_SECRET_KEY über Ihre Prozessumgebung bereit.
Verwenden Sie dabei Zugangsdaten mit der Berechtigung s3:GetObject für das
gewählte Objekt. Nutzen Sie den S3-API-Endpunkt, nicht die Adresse der Webkonsole. Die Region muss
mit Ihrem Server übereinstimmen. HTTP und die obigen Root-Zugangsdaten sind nur für die
Loopback-Fixture bestimmt; nutzen Sie für Ihren verwalteten Dienst HTTPS und den Mechanismus Ihrer
Organisation zur Bereitstellung von Zugangsdaten. Deaktivieren Sie die Zertifikatsprüfung nicht,
um einen Truststore-Fehler zu umgehen.
Sobald die Umgebung konfiguriert ist, fügen Sie Folgendes im Projektstamm ein. Ersetzen Sie dabei Bucket und Schlüssel durch die Angaben zu einem bereits vorhandenen Objekt:
bash download.sh my-bucket 'reports/April report.bin' './April report.bin'
Häufige Ausnahmen behandeln
Status 0 bedeutet, dass die gesamte Antwort gelesen, die Ressourcen geschlossen und die Zieldatei bereitgestellt wurden. Status 1 bedeutet, dass der Befehl fehlgeschlagen ist; die Diagnose lässt Zugangsdaten, Antwortkörper und Stacktraces bewusst weg. HTTP 404 kann auf einen fehlenden Bucket oder exakten Schlüssel hinweisen. HTTP 403 kann auf ungültige Zugangsdaten oder einen verweigerten Objektzugriff hinweisen. Fehler bei lokalen Pfaden und unvollständige Transfers führen ebenfalls zu einem Fehlerstatus.
Das Verbindungs-Timeout beträgt 10 Sekunden, die Lese- und Schreib-Timeouts betragen 30 Sekunden.
Dafür werden die Timeout-Parameter des SDK in Millisekunden
verwendet. Diese Timeouts gelten für einzelne Vorgänge, nicht als Frist für den gesamten Download.
Es gibt keine Wiederholungsschleife auf Anwendungsebene: Führen Sie nach einem vorübergehenden
Fehler den gesamten Befehl erneut aus, ohne dass eine Zieldatei vorhanden ist. Jeder Versuch
beginnt mit einer neuen Staging-Datei. Eine Wiederholung nur um getObject
würde Fehler beim Lesen des Antwortkörpers nicht erfassen.
Fehlerbehebung
Verbindung abgelehnt
Prüfen Sie API-Host und Port sowie, ob der Server läuft und von Ihrem Rechner aus erreichbar ist.
Sehen Sie sich für die lokale Demo demo-server.log an; ein belegter Port muss
geändert werden, bevor eine weitere Fixture gestartet wird. Ein fehlgeschlagener Build startet
keinen Download. Beheben Sie daher zuerst den Fehler aus der Maven-Diagnose.
Zugriff verweigert
Prüfen Sie die Objektberechtigung des Kontos, die Region sowie die exakte Groß- und Kleinschreibung und Zeichensetzung des Schlüssels. Der Importer listet weder ein Präfix auf, noch wählt er das erste passende Objekt aus. Wenn eine Zieldatei bereits existiert, wählen Sie einen neuen lokalen Dateinamen, statt ein ungeprüftes früheres Ergebnis zu löschen.
Für einen Import in eine gehostete Verarbeitungspipeline ist der Robot 🤖 /minio/import eine separate Integrationsoption. Der lokale Befehl oben erzeugt eine Datei für Ihren eigenen Java-Workflow.
