Exportar arquivos para Microsoft Azure em Rust
Faça upload de um arquivo local de até 16 MiB para um contêiner privado existente do Azure Blob Storage com este programa completo em Rust. Ele envia uma única requisição condicional e se recusa a substituir um blob existente. Arquivos vazios e dados binários são entradas válidas.
Este é um exemplo de manutenção para o crate não oficial azure_storage_blobs 0.21.0.
O aviso da Microsoft sobre o crate legado
informa que a empresa não planeja atualizar esse crate. Para novas aplicações, avalie o
SDK oficial azure_storage_blob,
cujos exemplos de autenticação usam Microsoft Entra ID. Sua API difere da usada neste exemplo com
versão fixada; alterar apenas o nome da dependência não constitui uma migração.
Pré-requisitos
- Um shell Linux com Bash, Rust, Cargo e o vinculador do sistema necessário para sua instalação do Rust. Este exemplo foi testado com Rust e Cargo 1.98.1, usando a edição 2021. Esse é um ambiente testado, não uma versão mínima do compilador. Consulte o guia de instalação do Rust para instalar uma cadeia de ferramentas atual.
- Um compilador C,
pkg-confige as bibliotecas e os cabeçalhos do OpenSSL. O transporte TLS padrão precisa deles no Linux; o guia de compilação do crate OpenSSL lista os pacotes para distribuições comuns. Instale esses pré-requisitos antes de criar o projeto. - Uma conta do Azure Storage que já permita autenticação Shared Key, além do nome e da chave de acesso dessa conta. Este procedimento se destina a uma integração Shared Key existente. A Microsoft recomenda Microsoft Entra ID para novas aplicações; não habilite Shared Key apenas para seguir este exemplo.
- Um contêiner privado existente, provisionado separadamente. O programa não cria contêineres nem altera suas configurações de acesso. Um contêiner privado exige autorização para ler seus blobs, conforme descrito na documentação do Azure sobre acesso anônimo.
- Um arquivo regular completo que permaneça inalterado durante o upload. Os argumentos do comando, incluindo o caminho local, devem ser UTF-8 válido. Use este exemplo de forma sequencial.
Configuração do projeto
Execute este bloco em um diretório com permissão de escrita. Ele verifica as ferramentas de
compilação e cria um novo diretório; se azure_export já existir, ele para sem entrar
nesse projeto nem alterá-lo.
(
command -v cargo >/dev/null &&
command -v rustc >/dev/null &&
command -v cc >/dev/null &&
command -v pkg-config >/dev/null &&
pkg-config --exists openssl &&
mkdir azure_export &&
mkdir azure_export/src
) && cd azure_export
Continue somente após a conclusão bem-sucedida desse bloco. Salve o conteúdo a seguir como
Cargo.toml. O [workspace] vazio mantém este projeto separado de
um espaço de trabalho do Cargo que o englobe. Os recursos de sistema de arquivos e de E/S do Tokio
são explícitos. Mantenha o Cargo.lock gerado junto à sua aplicação para preservar
as dependências transitivas resolvidas.
Se a criação do diretório falhar antes de terminar, inspecione o que foi criado antes de tentar novamente. Use um novo diretório pai com permissão de escrita para uma nova tentativa; o bloco preserva deliberadamente os arquivos existentes.
[package]
name = "azure_export"
version = "0.1.0"
edition = "2021"
[workspace]
[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"] }
Inicialização do cliente Azure
Forneça AZURE_STORAGE_ACCOUNT e AZURE_STORAGE_KEY pelo seu gerenciador de
segredos ou pelo ambiente do processo. A chave é uma chave de acesso da conta de armazenamento,
não uma string de conexão. O programa chama StorageCredentials::access_key(); ele não carrega uma
sessão autenticada da CLI do Azure. Mantenha a chave fora do controle de versão e dos argumentos
de comandos do terminal.
Salve o programa completo abaixo como src/main.rs.
Implementação do upload de arquivo
O programa lê no máximo 16 MiB mais um byte antes de enviar uma requisição e rejeita uma entrada
acima desse tamanho. O teto de 16 MiB é uma política deste exemplo, não o limite de armazenamento
do Azure. Ele mantém a entrada em memória para uma única operação put_block_blob;
não divide o arquivo em blocos intermediários nem faz upload em paralelo. O nome do blob de
destino é um argumento separado do caminho local.
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);
}
}
Coloque o arquivo que deseja exportar em data.bin no diretório do projeto.
Com as credenciais fornecidas e o contêiner privado rust-exports já disponível,
execute:
cargo run -- rust-exports 'exports/data.bin' './data.bin'
O Cargo resolve as dependências, compila o programa e o executa. Os três argumentos são o
contêiner, o nome do blob de destino e o caminho do arquivo local. Coloque entre aspas os caminhos
que contêm espaços. Uma requisição bem-sucedida imprime Upload confirmed. e encerra o
processo com código zero. O blob recebe o nome exports/data.bin; a barra faz parte
do nome, independentemente da estrutura de diretórios local. Escolha um nome de blob sem barras
no início ou no fim: este SDK remove esses caracteres.
O prazo de 60 segundos abrange a leitura do arquivo local e o upload pelo SDK, após a construção do cliente. A instalação de dependências e a compilação pelo Cargo ficam fora desse prazo. Mesmo um arquivo abaixo do teto de tamanho pode exceder esse prazo em uma conexão lenta.
Tratamento de erros
Uma falha na leitura local, uma configuração ausente, uma requisição rejeitada ou o esgotamento do
prazo faz o processo encerrar com código diferente de zero e emitir o diagnóstico genérico
Upload was not confirmed. O programa evita deliberadamente imprimir credenciais ou respostas
brutas do SDK. Verifique primeiro o arquivo local e seu tamanho, depois a conta selecionada, a
chave, o contêiner e o destino. Esse diagnóstico, por si só, não identifica o motivo da recusa do
Azure nem comprova que nenhum blob foi gravado.
A opção IfMatchCondition::NotMatch("*") envia If-None-Match: *. O
contrato de cabeçalhos condicionais do Azure
permite a gravação somente quando o destino não existe. Executar o comando novamente para um
destino existente falha e preserva seu conteúdo anterior. Escolha outro nome de blob para uma
nova exportação em vez de excluir um objeto existente para fazer o comando funcionar.
As novas tentativas automáticas do SDK estão desabilitadas. Após o esgotamento do prazo, a perda da conexão ou uma interrupção, o Azure ainda pode concluir um upload que já recebeu. Encerrar o processo local não cancela essa gravação remota. Não tente novamente às cegas nem trate uma listagem vazia como prova de que uma gravação pendente não poderá aparecer depois. Inspecione o destino exato por meio de um cliente autenticado e reconcilie o estado da primeira operação antes de tentar novamente; uma recusa posterior pode significar que o primeiro upload foi bem-sucedido.
Verificar o resultado armazenado
Upload confirmed. significa que o SDK recebeu uma resposta de upload bem-sucedido.
O programa não faz download do blob nem compara os bytes armazenados. Para uma exportação que
precise verificar, faça download desse blob exato por meio de um cliente autenticado e compare o
arquivo inteiro baixado com o arquivo de origem inalterado, inclusive se a origem tiver zero
bytes. Verificar apenas o nome ou a contagem de bytes não permite detectar conteúdos diferentes
do mesmo tamanho. Mantenha o arquivo de origem local até concluir a verificação.
A privacidade do contêiner é um pré-requisito separado: um upload bem-sucedido não comprova sua política de acesso. Este exemplo não oferece uploads retomáveis nem um supervisor de transferências para produção. Para arquivos maiores ou autenticação baseada em identidade, comece pelos exemplos de upload e de credenciais do SDK oficial, em vez de ampliar o uso das versões legadas fixadas sem uma revisão de migração.
