Dateien mit Rust nach Microsoft Azure exportieren
Dieses Beispiel lädt Dateien bis zu 16 MiB in einen bestehenden privaten Container in Azure Blob
Storage hoch. Es verwendet die inoffizielle API von azure_storage_blobs in der festgelegten
Version 0.21.0. Laut Microsofts Hinweis zu diesem Legacy-Crate
erhält es keine Updates mehr; seine API unterscheidet sich vom neueren SDK. Nutzen Sie dieses Beispiel
für die Wartung von Anwendungen mit diesem Crate und ziehen Sie für neue Anwendungen das
aktuelle Azure SDK in Betracht.
Voraussetzungen
- Rust und Cargo; das Beispiel wurde mit Rust 1.98.1 (Edition 2021) kompiliert.
- Ein Azure-Storage-Konto, das die Authentifizierung mit Shared Key erlaubt.
- Ein bestehender privater Container, den Ihr Bereitstellungsprozess erstellt hat.
- Eine vollständige lokale Datei, die während des Uploads nicht verändert wird.
Projekt einrichten
Erstellen Sie das Projekt:
cargo new azure_export --edition 2021 --vcs none
cd azure_export
Ersetzen Sie Cargo.toml durch den folgenden Inhalt. Die Dateisystem- und E/A-Features von Tokio werden explizit angegeben. Nehmen Sie die generierte Cargo.lock in die Versionsverwaltung Ihrer Anwendung auf, um die Versionen transitiver Abhängigkeiten beizubehalten.
[package]
name = "azure_export"
version = "0.1.0"
edition = "2021"
[dependencies]
azure_core = "=0.21.0"
azure_storage = "=0.21.0"
azure_storage_blobs = "=0.21.0"
tokio = { version = "=1.48.0", features = ["fs", "io-util", "macros", "rt-multi-thread", "time"] }
Azure-Client initialisieren
Setzen Sie AZURE_STORAGE_ACCOUNT und AZURE_STORAGE_KEY mithilfe Ihrer Verwaltung für Secrets oder der
Prozessumgebung. Der Schlüssel ist ein Zugriffsschlüssel für das Speicherkonto, keine
Verbindungszeichenfolge. Dieses Beispiel verwendet StorageCredentials::access_key(); es lädt keine
Zugangsdaten der Azure CLI.
Speichern Sie das vollständige Programm unten als src/main.rs.
Datei-Upload implementieren
Die Datei wird mit einer Begrenzung auf 16 MiB eingelesen, bevor eine Anfrage gesendet wird.
Dabei wird eine einzelne gepufferte Anfrage vom Typ put_block_blob gesendet;
der Upload wird weder automatisch in Blöcke aufgeteilt noch parallel ausgeführt. Der Name des
Ziel-Blobs ist ein separates Argument, damit lokale Verzeichnisnamen nicht versehentlich als
Blob-Namen verwendet werden.
use azure_core::{prelude::IfMatchCondition, RetryOptions};
use azure_storage::StorageCredentials;
use azure_storage_blobs::prelude::{BlobServiceClient, ClientBuilder};
use std::{env, error::Error, path::Path, time::Duration};
use tokio::{fs::File, io::AsyncReadExt, time::timeout};
type AppResult<T> = Result<T, Box<dyn Error + Send + Sync>>;
const MAX_BYTES: u64 = 16 * 1024 * 1024;
async fn upload_file(
service: &BlobServiceClient,
container: &str,
blob_name: &str,
path: &Path,
) -> AppResult<()> {
if container.is_empty() || blob_name.is_empty() {
return Err("Container and blob name must not be empty".into());
}
let file = File::open(path).await?;
if !file.metadata().await?.is_file() {
return Err("Input must be a regular file".into());
}
let mut contents = Vec::new();
file.take(MAX_BYTES + 1).read_to_end(&mut contents).await?;
if contents.len() as u64 > MAX_BYTES {
return Err("Input exceeds the 16 MiB example limit".into());
}
let blob = service.container_client(container).blob_client(blob_name);
blob.put_block_blob(contents)
.content_type("application/octet-stream")
.if_match(IfMatchCondition::NotMatch("*".to_owned()))
.await?;
Ok(())
}
async fn run() -> AppResult<()> {
let args: Vec<String> = env::args().skip(1).collect();
if args.len() != 3 {
return Err("Usage: azure_export CONTAINER BLOB_NAME FILE".into());
}
let account = env::var("AZURE_STORAGE_ACCOUNT")?;
let key = env::var("AZURE_STORAGE_KEY")?;
let credentials = StorageCredentials::access_key(account.clone(), key);
let service = ClientBuilder::new(account, credentials)
.retry(RetryOptions::none())
.blob_service_client();
timeout(
Duration::from_secs(60),
upload_file(&service, &args[0], &args[1], Path::new(&args[2])),
)
.await??;
println!("Upload confirmed.");
Ok(())
}
#[tokio::main]
async fn main() {
if run().await.is_err() {
eprintln!("Upload was not confirmed; check the input, credentials, container, and destination.");
std::process::exit(1);
}
}
Wenn dem Prozess die Zugangsdaten übergeben wurden und der Container rust-exports bereits bereitgestellt ist, führen Sie Folgendes aus:
printf 'Example export\n' > data.txt
cargo run -- rust-exports data.txt data.txt
Fehlerbehandlung
Dateisystem-, Zugangsdaten-, SDK- und Zeitüberschreitungsfehler werden über einen gemeinsamen Ergebnistyp der Anwendung weitergereicht. Die CLI gibt einen Exit-Status ungleich null zurück, ohne Zugangsdaten oder unverarbeitete Serverantworten auszugeben. Retries sind explizit deaktiviert, damit ein ungewisser Ausgang sichtbar wird.
Die bedingte Anfrage verhindert das Überschreiben eines bestehenden Blobs. Nach einer Zeitüberschreitung oder einer verlorenen Antwort kann der Server den Upload dennoch angenommen haben. Prüfen Sie das Ziel vor einem erneuten Versuch; dieser kann fehlschlagen, weil die erste Anfrage den Ziel-Blob bereits erstellt hat. Eine Zeitüberschreitung beendet das lokale Warten, jedoch keinen entfernten Schreibvorgang, den Azure bereits empfangen hat.
Bewährte Verfahren
Verwenden Sie einen dedizierten privaten Container und beschränken Sie, wer den Kontoschlüssel lesen darf. Nehmen Sie ihn nicht in die Versionsverwaltung auf. Hier wird die Authentifizierung mit Shared Key gezeigt; der Wechsel zu identitätsbasierten Zugangsdaten erfordert einen gezielt konfigurierten Anbieter für Zugangsdaten und entsprechende Berechtigungen.
Lassen Sie lokale Eingabedateien bis zum Abschluss des Vorgangs unverändert. Implementieren Sie für größere Dateien das schrittweise Hochladen von Blöcken und einen abschließenden Commit der Blockliste mithilfe der APIs des Crates in der festgelegten Version. Alternativ wählen Sie ein gepflegtes SDK mit einer geeigneten API für Streaming-Uploads.
Hinweise für den Produktivbetrieb
Stellen Sie den Container separat bereit und deaktivieren Sie dabei den öffentlichen Zugriff. Dieses Programm erstellt weder Container noch ändert es deren Zugriffsrichtlinien. Ein fehlender Container oder unzureichende Berechtigungen führen zu einem fehlgeschlagenen Upload und sind kein Grund, stillschweigend Infrastruktur zu erstellen.
Der bedingte Schreibzugriff und das feste Speicherlimit machen das Verhalten des Beispiels vorhersehbar, ermöglichen jedoch keine fortsetzbaren Uploads. Erfassen Sie bestätigte Uploads in Ihrer Anwendung und überwachen Sie Fehler, ohne sensible Zugangsdaten zu protokollieren.
Für einen verwalteten Workflow kann der Robot für den Azure-Export von Transloadit verarbeitete Dateien nach Azure Blob Storage exportieren.
