Dateien mit PHP nach Amazon S3 exportieren: umfassende Anleitung
Um eine lokale Datei mit PHP nach Amazon S3 zu exportieren, installieren Sie das AWS SDK und rufen
Sie putObject mit einem SourceFile auf.
Diese Anleitung entwickelt ein CLI-Skript, das private Objekte mit Metadaten und serverseitiger
Verschlüsselung speichert, Fehler über seinen Exit-Status meldet und Multipart-Uploads unterstützt.
Außerdem laden Sie das Objekt unabhängig herunter, um zu prüfen, ob S3 exakt die Bytes erhalten hat.
Voraussetzungen und Einrichtung
Verwenden Sie eine Bash-Shell unter Linux mit installiertem PHP, Composer 2 und AWS CLI v2. Die Beispiele wurden mit PHP 8.5.10, Composer 2.10.3, AWS CLI 2.37.10 und AWS SDK for PHP 3.399.2 getestet. PHP 8.5 ist ein gepflegter Release-Zweig; verwenden Sie für Neuinstallationen dessen aktuelle Patch-Version. Diese Versionsangaben dokumentieren die Tests und nicht die Mindestanforderungen des SDK.
Das SDK benötigt PHP 8.1 oder neuer und SimpleXML.
Diese Anleitung verwendet außerdem die PHP-Erweiterungen cURL und OpenSSL für HTTPS. Installieren
Sie fehlende Werkzeuge mithilfe der PHP-Installationsanleitung,
der Download-Anleitung für Composer und der
Installationsanleitung für AWS CLI v2.
GNU cmp dient zum Dateivergleich und ist
im Paket GNU Diffutils enthalten.
Prüfen Sie alle Voraussetzungen, bevor Sie Projektdateien erstellen:
(
for tool in php composer aws cmp; do
if ! command -v "$tool" >/dev/null 2>&1; then
printf 'Missing prerequisite: %s\n' "$tool" >&2
exit 1
fi
done
php -r 'foreach (["simplexml", "curl", "openssl"] as $extension) {
if (!extension_loaded($extension)) {
fwrite(STDERR, "Missing PHP extension: $extension\n"); exit(1);
}
}'
)
AWS SDK for PHP installieren
Arbeiten Sie in einem neuen, leeren Verzeichnis außerhalb des öffentlich zugänglichen
Webverzeichnisses Ihrer Anwendung. Speichern Sie Folgendes als composer.json;
ersetzen Sie dabei nicht das Manifest einer bestehenden Anwendung:
{
"require": {
"aws/aws-sdk-php": "3.399.2"
}
}
Installieren Sie die getestete SDK-Version im Ordner vendor/ dieses
Verzeichnisses:
COMPOSER=composer.json COMPOSER_VENDOR_DIR=vendor \
composer install --no-interaction --no-plugins --no-scripts
Bewahren Sie die erzeugte Datei composer.lock für reproduzierbare Installationen
auf. Schlägt die Installation fehl, beheben Sie das gemeldete Problem mit den Voraussetzungen oder
dem Netzwerk und wiederholen Sie denselben Befehl in diesem Verzeichnis. Sie müssen weder das
Manifest noch die Lockdatei löschen. Die
Anleitung zu den Grundlagen von Composer erklärt, wie die Lockdatei
Abhängigkeiten auf bestimmte Versionen festlegt.
AWS-Zugangsdaten konfigurieren
Verwenden Sie ein bestehendes, autorisiertes AWS-Profil. CLI und SDK unterstützen beide den Anbieter für gemeinsam genutzte Zugangsdaten. Wählen Sie das Profil und die Region Ihres Buckets und prüfen Sie dann vor dem Upload das Konto:
export AWS_PROFILE=default
export AWS_DEFAULT_REGION=us-east-1
aws sts get-caller-identity --query Account --output text
Ersetzen Sie default und us-east-1 durch Ihre eigenen
Werte. Sind die Zugangsdaten abgelaufen, erneuern Sie Ihre autorisierte Sitzung, bevor Sie
fortfahren. Zugangsdaten aus Umgebungsvariablen haben Vorrang vor Profilen. Prüfen Sie daher bei
einem unerwarteten Konto, ob unbeabsichtigt Zugangsdaten als Umgebungsvariablen exportiert wurden.
Schreiben Sie niemals Zugriffsschlüssel in die PHP-Datei. Bevorzugen Sie für Anwendungen, die auf
AWS gehostet werden, eine IAM-Rolle für den Workload statt langlebiger Schlüssel.
Einen einfachen S3-Bucket erstellen
Das Skript verwendet einen bestehenden privaten S3-Bucket für allgemeine Zwecke. Falls Sie einen
benötigen, lassen Sie Ihren Bucket-Administrator die
AWS-Anleitung zur Bucket-Erstellung befolgen.
Lassen Sie „Öffentlichen Zugriff blockieren“ aktiviert und stellen Sie „Objekteigentümerschaft“ auf
„Bucket-Eigentümer erzwungen“. Verwenden Sie in AWS_DEFAULT_REGION die tatsächliche
Region des Buckets; die Bucket-Verwaltung erfolgt getrennt vom Exportskript.
Legen Sie für die folgenden Befehle einen Bucket-Namen und einen neuen Objektschlüssel unter einem Präfix fest, das Ihnen gehört:
export S3_BUCKET=your-private-bucket
export S3_KEY=php-export-test/sample.bin
Ersetzen Sie beide Beispielwerte. Ein Upload unter einem bestehenden Schlüssel ersetzt dessen aktuelles Objekt oder erstellt bei aktivierter Versionierung eine neue Version. Verwenden Sie für diese Tests eindeutige Schlüssel und führen Sie jeweils nur einen Export aus.
Ein PHP-Skript zum Dateiexport entwickeln
Speichern Sie dieses vollständige Programm als export.php neben
composer.json. Seine Argumente sind die lokale Datei, der Bucket, der
Objektschlüssel und ein optionaler Modus, entweder put oder
multipart:
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use Aws\Exception\AwsException;
use Aws\Exception\MultipartUploadException;
use Aws\S3\MultipartUploader;
use Aws\S3\S3Client;
$mode = $argv[4] ?? 'put';
if ($argc < 4 || $argc > 5 || !in_array($mode, ['put', 'multipart'], true)) {
fwrite(STDERR, "Usage: php export.php LOCAL_FILE BUCKET KEY [put|multipart]\n");
exit(2);
}
[$script, $sourceFile, $bucket, $key] = $argv;
$region = getenv('AWS_DEFAULT_REGION');
if (!$region || $bucket === '' || $key === '') {
fwrite(STDERR, "Set AWS_DEFAULT_REGION and provide a nonempty bucket and key.\n");
exit(2);
}
if (!is_file($sourceFile) || !is_readable($sourceFile)) {
fwrite(STDERR, "Cannot read source file: $sourceFile\n");
exit(2);
}
try {
$s3Client = new S3Client([
'version' => '2006-03-01',
'region' => $region,
'retries' => 0,
'http' => ['connect_timeout' => 10, 'timeout' => 60],
]);
$options = [
'Bucket' => $bucket,
'Key' => $key,
'ContentType' => 'application/octet-stream',
'Metadata' => [
'original-filename' => rawurlencode(basename($sourceFile)),
'upload-date' => gmdate('c'),
],
'ServerSideEncryption' => 'AES256',
'CacheControl' => 'no-store',
];
if ($mode === 'multipart' && filesize($sourceFile) > 0) {
$uploader = new MultipartUploader($s3Client, $sourceFile, [
'bucket' => $bucket,
'key' => $key,
'part_size' => 5 * 1024 * 1024,
'concurrency' => 1,
'params' => $options,
]);
$uploader->upload();
} else {
$s3Client->putObject($options + ['SourceFile' => $sourceFile]);
}
echo "Export complete: s3://$bucket/$key\n";
} catch (Throwable $e) {
if (isset($uploader)) {
$id = $uploader->getState()->getId();
if (!empty($id['UploadId'])) {
try {
$s3Client->abortMultipartUpload($id);
fwrite(STDERR, "Aborted incomplete multipart upload.\n");
} catch (Throwable $abortError) {
fwrite(STDERR, "Abort failed; clean up upload ID: {$id['UploadId']}\n");
}
}
}
if ($e instanceof MultipartUploadException) {
fwrite(STDERR, "Multipart export failed for: $sourceFile\n");
} elseif ($e instanceof AwsException) {
fwrite(STDERR, 'S3 export failed: ' . ($e->getAwsErrorCode() ?? 'request error') . "\n");
fwrite(STDERR, 'Request ID: ' . ($e->getAwsRequestId() ?? 'unavailable') . "\n");
} else {
fwrite(STDERR, "Export failed; check the local file, SDK installation, and credentials.\n");
}
exit(1);
}
Erstellen Sie eine kleine Fixture und laden Sie sie hoch:
printf 'S3 export check\n' > sample.bin &&
php export.php ./sample.bin "$S3_BUCKET" "$S3_KEY"
Das Skript gibt Export complete gefolgt von der S3-URI des privaten Objekts erst aus, nachdem das SDK die Anfrage abgeschlossen hat. Die URI identifiziert das Objekt; sie ist kein öffentlicher Download-Link. Bei einem fehlgeschlagenen Export schreibt das Programm Diagnosemeldungen nach stderr und beendet sich mit einem Exit-Status ungleich null. Es liest Ihre lokale Datei, ohne sie zu löschen. Lassen Sie diese Datei unverändert, bis die Überprüfung abgeschlossen ist.
Dateimetadaten effizient verwalten
Das Array $options verwendet für beide Upload-Modi dieselben Metadaten,
denselben Inhaltstyp, dieselbe Cache-Richtlinie und dieselbe SSE-S3-Verschlüsselung.
original-filename ist prozentkodiert, damit Leerzeichen und Nicht-ASCII-Zeichen in
einem HTTP-Metadaten-Header dargestellt werden können. upload-date speichert
einen UTC-Zeitstempel. S3-Schlüssel sind unabhängig von lokalen Dateinamen. Behalten Sie daher den
vollständigen Schlüssel bei, wenn Sie eine exportierte Datei suchen.
application/octet-stream eignet sich für beliebige Binärdateien. Legen Sie einen genaueren
Wert für ContentType fest, wenn Ihre Anwendung das Format kennt, und wählen Sie
CacheControl passend zu den Anwendungen, die die Datei nutzen. Das Beispiel
verwendet no-store. AES256 fordert explizit
SSE-S3-Verschlüsselung an;
dies gewährt keinen öffentlichen Zugriff und ersetzt keine Zugriffskontrollen.
Ihre Integration testen und Fehler beheben
Prüfen Sie die gespeicherten Metadaten und die Verschlüsselung und laden Sie das Objekt dann in ein neues lokales Prüfverzeichnis herunter:
aws s3api head-object --bucket "$S3_BUCKET" --key "$S3_KEY" \
--region "$AWS_DEFAULT_REGION" \
--query '{bytes:ContentLength,type:ContentType,encryption:ServerSideEncryption,metadata:Metadata}'
(
mkdir s3-verify &&
aws s3api get-object --bucket "$S3_BUCKET" --key "$S3_KEY" \
--region "$AWS_DEFAULT_REGION" s3-verify/download.bin &&
cmp -- ./sample.bin s3-verify/download.bin
)
Die Metadatenantwort sollte AES256, Ihren Dateinamen und die erwartete
Byteanzahl enthalten. cmp gibt null zurück, wenn jedes Byte
übereinstimmt, auch bei einer leeren Datei. Verwenden Sie für jeden Durchlauf ein neues
Prüfverzeichnis. Schlägt ein Download fehl, entfernen Sie nur das von Ihnen erstellte temporäre
Verzeichnis s3-verify und wiederholen Sie dann den Block. Dieser lehnt ein
bestehendes Verzeichnis ab, um frühere Downloads zu schützen. Verwenden Sie den S3-ETag nicht als
allgemeinen Datei-Hash; ETags von Multipart-Objekten können eine andere Bedeutung haben.
Testen Sie eine eigene Binärdatei, die keine Textdatei ist, und eine leere Datei mit unterschiedlichen Schlüsseln, bevor Sie das Skript in einen Job einbinden. Erfassen Sie in einem geplanten Job stderr und prüfen Sie den Exit-Status des Prozesses. Halten Sie Zugangsdaten, signierte URLs und unverarbeitete SDK-Ausnahmemeldungen aus Protokollen heraus, die für Nutzer zugänglich sind.
Fortgeschrittene Anwendungsfälle: Multipart-Uploads
AWS empfiehlt Multipart-Uploads für Objekte über 100 MB.
Mit dem Skript können Sie diesen Ablauf auch mit einer kleineren Fixture testen: Wählen Sie eine
Datei größer als 5 MiB, damit sie mindestens zwei Teile umfasst, speichern Sie sie als
sample.bin und wählen Sie einen neuen Schlüssel:
php export.php ./sample.bin "$S3_BUCKET" "$S3_KEY" multipart
Führen Sie anschließend dieselben Metadaten- und Byteprüfungen durch. Für diese Fixture verwendet
der Dateiuploader Teile mit 5 MiB und überträgt jeweils einen Teil. Er wartet auf
CompleteMultipartUpload, bevor er Erfolg meldet. Bei wesentlich größeren Dateien kann das
SDK die Teilgröße erhöhen. Leere Dateien verwenden putObject auch dann, wenn
Sie den Multipart-Modus wählen, da sie keine Teile zum Zusammenfügen enthalten.
Retries sind deaktiviert. Jede HTTP-Anfrage hat einen Verbindungs-Timeout von 10 Sekunden und einen Übertragungs-Timeout von 60 Sekunden. Diese Grenzen gelten pro Anfrage, nicht für die Gesamtdauer des Multipart-Uploads. Eine langsame Verbindung kann selbst bei einer kleinen Datei zu einem Fehler führen. Dieses Beispiel bricht fehlgeschlagene Multipart-Uploads ab, statt sie fortzusetzen. Beheben Sie das Problem und führen Sie das Skript mit derselben lokalen Quelldatei erneut aus. Schlägt auch der Abbruch fehl, enthält stderr die Upload-ID, damit Ihr Bucket-Administrator den Upload entfernen kann. Hochgeladene Teile verursachen Speicherkosten, bis der Upload abgeschlossen oder abgebrochen wird, wie die Multipart-Übersicht von AWS erklärt.
Geht die Abschlussantwort verloren, kann das Ergebnis unklar bleiben: S3 hat das Objekt möglicherweise bereits gespeichert, obwohl der Client einen Fehler meldet. Prüfen Sie den exakten Schlüssel und die Bytes vor einem erneuten Versuch. Ein nicht abgeschlossener Multipart-Upload ist kein fertiges Objekt.
Bewährte Verfahren für die Sicherheit
Gewähren Sie der Exportidentität s3:PutObject für das ihr zugeordnete Präfix und
s3:AbortMultipartUpload für die Bereinigung nach Fehlern. Die Prüfidentität benötigt
zusätzlich s3:GetObject, um das Objekt zu prüfen und herunterzuladen.
Beschränken Sie diese Berechtigungen auf den vorgesehenen Bucket und die vorgesehenen Schlüssel.
Das Skript benötigt keine Berechtigung, Buckets zu erstellen oder deren Richtlinien zu ändern.
AWS führt die relevanten Berechtigungen in seiner
Multipart-Übersicht auf.
Das Beispiel sendet keine ACL und setzt einen privaten Bucket voraus. Es verwendet explizit SSE-S3. Eine Bucket-Richtlinie, die SSE-KMS verlangt, erfordert eine gezielte Änderung der Verschlüsselungskonfiguration und passende KMS-Berechtigungen. Lassen Sie die Prüfung von TLS-Zertifikaten aktiviert.
Häufige Probleme beheben
- AccessDenied: Prüfen Sie das ausgewählte Konto, die Präfixberechtigungen und die Verschlüsselungsrichtlinie des Buckets.
- Regions- oder Endpunktfehler: Stimmen Sie
AWS_DEFAULT_REGIONauf die Region des Buckets ab. - Fehlende Datei: Übergeben Sie einen lesbaren lokalen Pfad. Setzen Sie Dateinamen mit
Leerzeichen in Anführungszeichen. Stellen Sie einem Dateinamen, der mit einem Bindestrich beginnt,
in der Shell
./voran. - Multipart-Fehler: Prüfen Sie stderr auf einen fehlgeschlagenen Abbruch. Behandeln Sie einen verbleibenden Multipart-Upload nicht als gespeicherte Datei und prüfen Sie einen unklaren Abschluss vor einem erneuten Versuch.
- Abweichende Bytes: Stellen Sie sicher, dass Schlüssel und lokale Datei mit denen Ihres Uploads übereinstimmen, und prüfen Sie, ob ein anderer Schreibzugriff das Objekt ersetzt hat.
Fazit und nächste Schritte
Sobald das private Objekt den Vergleich nach dem Download besteht, übernehmen Sie dieselbe Behandlung von Uploads und Exit-Status in Ihren PHP-Job. Behalten Sie unabhängige Byteprüfungen in Ihren Integrationstests bei und bewahren Sie lokale Quelldateien auf, bis der Export bestätigt ist. Um eine fehlgeschlagene Multipart-Übertragung fortzusetzen, beginnen Sie mit der Dokumentation zur Wiederaufnahme mit MultipartUploader von AWS. Entscheiden Sie dann, wie Ihre Anwendung den Upload-Zustand aufbewahrt und zurückgelassene Teile bereinigt.
