Autor: Dante Testa (https://dantetesta.com.br)
Data: 2025-01-13
Versão: 1.0
Este documento foi criado após resolver todos os problemas reais de integração com Bunny.net. Siga EXATAMENTE estas instruções para evitar erros comuns.
- Bunny Storage (Arquivos/Downloads)
- Bunny Stream (Vídeos)
- Erros Comuns e Soluções
- Checklist de Verificação
- Scripts de Debug
# .env
BUNNY_STORAGE_ZONE=nome-da-zona # ⚠️ NOME, não o ID numérico!
BUNNY_STORAGE_KEY=abc123-def456-... # Password da Storage Zone
BUNNY_CDN_URL=https://nome-da-zona.b-cdn.net # URL do Pull Zone (CDN)- BUNNY_STORAGE_ZONE = NOME da zona (ex: "meusite"), NÃO o ID numérico (ex: 1256199)
- BUNNY_STORAGE_KEY = Password específico da Storage Zone (não é a API Key geral)
- Encontre em: https://dash.bunny.net/storage → Clique na zona → "FTP & API Access"
Bunny Storage tem regiões diferentes. A URL base muda conforme a região:
| Região | URL Base | Código |
|---|---|---|
| Europa (Frankfurt) | https://storage.bunnycdn.com |
(vazio) |
| Europa (London) | https://uk.storage.bunnycdn.com |
uk |
| América do Norte (NY) | https://ny.storage.bunnycdn.com |
ny |
| América do Norte (LA) | https://la.storage.bunnycdn.com |
la |
| América do Sul (BR) | https://br.storage.bunnycdn.com |
br |
| Ásia (Singapore) | https://sg.storage.bunnycdn.com |
sg |
| Oceania (Sydney) | https://syd.storage.bunnycdn.com |
syd |
Como descobrir: Acesse https://dash.bunny.net/storage → Clique na zona → Veja "Region"
// NÃO USE ISSO!
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, $fileContent);/**
* Upload de arquivo para Bunny Storage
*
* Documentação: https://docs.bunny.net/reference/put_-storagezonename-path-filename
*/
public function uploadFile(string $localFilePath, string $remotePath): bool
{
// 1. Verificar se arquivo existe
if (!file_exists($localFilePath)) {
error_log("Bunny Upload Error: File not found - {$localFilePath}");
return false;
}
// 2. Abrir arquivo para leitura
$fileHandle = fopen($localFilePath, 'r');
if ($fileHandle === false) {
error_log("Bunny Upload Error: Cannot open file - {$localFilePath}");
return false;
}
$fileSize = filesize($localFilePath);
// 3. Montar URL (com região)
$region = 'br'; // Ajustar conforme a região da Storage Zone
$storageZone = $_ENV['BUNNY_STORAGE_ZONE']; // NOME, não ID!
$url = "https://{$region}.storage.bunnycdn.com/{$storageZone}/{$remotePath}";
// 4. Configurar cURL (EXATAMENTE assim!)
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PUT, true); // ✅ Usar PUT
curl_setopt($ch, CURLOPT_INFILE, $fileHandle); // ✅ Handle do arquivo
curl_setopt($ch, CURLOPT_INFILESIZE, $fileSize); // ✅ Tamanho
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'AccessKey: ' . $_ENV['BUNNY_STORAGE_KEY'],
'Content-Type: application/octet-stream'
]);
// 5. Executar
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
// 6. Limpar
curl_close($ch);
fclose($fileHandle);
// 7. Verificar resultado (201 = sucesso)
if ($httpCode !== 201) {
error_log("Bunny Upload Error: HTTP {$httpCode} - {$response} - {$error}");
error_log("Bunny Upload URL: {$url}");
return false;
}
return true;
}public function deleteFile(string $remotePath): bool
{
$region = 'br';
$storageZone = $_ENV['BUNNY_STORAGE_ZONE'];
$url = "https://{$region}.storage.bunnycdn.com/{$storageZone}/{$remotePath}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'AccessKey: ' . $_ENV['BUNNY_STORAGE_KEY']
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// 200 ou 204 = sucesso
return $httpCode === 200 || $httpCode === 204;
}public function getStorageUrl(string $remotePath): string
{
$cdnUrl = $_ENV['BUNNY_CDN_URL']; // Ex: https://meusite.b-cdn.net
return rtrim($cdnUrl, '/') . '/' . ltrim($remotePath, '/');
}Problema: Nomes com espaços, parênteses ou caracteres especiais causam erro:
❌ URL rejected: Malformed input to a URL function
Solução: Sempre sanitizar antes de fazer upload:
private function sanitizeFileName(string $fileName): string
{
// Separar nome e extensão
$pathInfo = pathinfo($fileName);
$name = $pathInfo['filename'];
$extension = $pathInfo['extension'] ?? '';
// Remover acentos
$name = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $name);
// Substituir espaços por underscores
$name = str_replace(' ', '_', $name);
// Remover caracteres especiais (manter apenas: a-z A-Z 0-9 _ -)
$name = preg_replace('/[^a-zA-Z0-9_-]/', '', $name);
// Limitar tamanho
$name = substr($name, 0, 100);
// Reconstruir
return $extension ? "{$name}.{$extension}" : $name;
}
// Uso:
$fileName = $this->sanitizeFileName($_FILES['file']['name']);Exemplos:
folder1 (1).pdf→folder11.pdfMeu Arquivo (2023).pdf→Meu_Arquivo_2023.pdfRelatório - Março.docx→Relatorio_-_Marco.docx
# .env
BUNNY_LIBRARY_ID=123456 # ID da Video Library
BUNNY_API_KEY=abc123-def456-... # API Key da conta Bunny-
BUNNY_LIBRARY_ID:
- Acesse: https://dash.bunny.net/stream
- Clique na sua Video Library
- O ID está na URL:
https://dash.bunny.net/stream/XXXXX(XXXXX é o ID)
-
BUNNY_API_KEY:
- Acesse: https://dash.bunny.net/account/settings
- Seção "API" → "API Key"
- NÃO confundir com Storage Key!
public function uploadVideo(string $localFilePath, string $title): ?string
{
$libraryId = $_ENV['BUNNY_LIBRARY_ID'];
$apiKey = $_ENV['BUNNY_API_KEY'];
// 1. Criar vídeo na biblioteca
$createUrl = "https://video.bunnycdn.com/library/{$libraryId}/videos";
$ch = curl_init($createUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['title' => $title]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'AccessKey: ' . $apiKey,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 && $httpCode !== 201) {
error_log("Bunny Create Video Error: HTTP {$httpCode} - {$response}");
return null;
}
$videoData = json_decode($response, true);
$videoGuid = $videoData['guid'] ?? null;
if (!$videoGuid) {
error_log("Bunny Create Video Error: No GUID returned");
return null;
}
// 2. Fazer upload do arquivo
$uploadUrl = "https://video.bunnycdn.com/library/{$libraryId}/videos/{$videoGuid}";
$fileHandle = fopen($localFilePath, 'r');
$fileSize = filesize($localFilePath);
$ch = curl_init($uploadUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, $fileHandle);
curl_setopt($ch, CURLOPT_INFILESIZE, $fileSize);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'AccessKey: ' . $apiKey,
'Content-Type: application/octet-stream'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
fclose($fileHandle);
if ($httpCode !== 200 && $httpCode !== 201) {
error_log("Bunny Upload Video Error: HTTP {$httpCode} - {$response}");
return null;
}
return $videoGuid;
}public function getPlayerUrl(string $videoGuid): string
{
$libraryId = $_ENV['BUNNY_LIBRARY_ID'];
return "https://iframe.mediadelivery.net/embed/{$libraryId}/{$videoGuid}?autoplay=false&preload=true";
}public function getVideoInfo(string $videoGuid): ?array
{
$libraryId = $_ENV['BUNNY_LIBRARY_ID'];
$apiKey = $_ENV['BUNNY_API_KEY'];
$url = "https://video.bunnycdn.com/library/{$libraryId}/videos/{$videoGuid}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'AccessKey: ' . $apiKey
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
return json_decode($response, true);
}
return null;
}public function getThumbnailUrl(string $videoGuid): string
{
$libraryId = $_ENV['BUNNY_LIBRARY_ID'];
// Obter info do vídeo
$videoInfo = $this->getVideoInfo($videoGuid);
// Verificar se foi processado (status 4 = pronto)
if ($videoInfo && ($videoInfo['status'] ?? 0) >= 4) {
$thumbnailFileName = $videoInfo['thumbnailFileName'] ?? 'thumbnail.jpg';
return "https://vz-{$libraryId}.b-cdn.net/{$videoGuid}/{$thumbnailFileName}";
}
return ''; // Ainda processando
}public function deleteVideo(string $videoGuid): bool
{
$libraryId = $_ENV['BUNNY_LIBRARY_ID'];
$apiKey = $_ENV['BUNNY_API_KEY'];
$url = "https://video.bunnycdn.com/library/{$libraryId}/videos/{$videoGuid}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'AccessKey: ' . $apiKey
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $httpCode === 200 || $httpCode === 204;
}public function updateVideo(string $videoGuid, array $data): bool
{
$libraryId = $_ENV['BUNNY_LIBRARY_ID'];
$apiKey = $_ENV['BUNNY_API_KEY'];
$url = "https://video.bunnycdn.com/library/{$libraryId}/videos/{$videoGuid}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'AccessKey: ' . $apiKey,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $httpCode === 200;
}Exemplo de uso:
// Atualizar título e descrição
$bunnyService->updateVideo($videoGuid, [
'title' => 'Novo Título',
'description' => 'Nova descrição'
]);Para substituir um vídeo, você precisa:
- Deletar o vídeo antigo
- Fazer upload do novo vídeo
// 1. Deletar vídeo antigo
$bunnyService->deleteVideo($oldVideoGuid);
// 2. Upload do novo vídeo
$newVideoGuid = $bunnyService->uploadVideo($newFile, 'Novo Título');- Tenho acesso ao painel Bunny.net
- Sei qual serviço vou usar (Storage ou Stream)
- Tenho as credenciais corretas
-
BUNNY_STORAGE_ZONEé o NOME (não ID) -
BUNNY_STORAGE_KEYé o Password da Storage Zone - Sei a região da Storage Zone (br, ny, uk, etc.)
- URL usa a região correta:
https://{region}.storage.bunnycdn.com - Estou usando
CURLOPT_PUT+CURLOPT_INFILE - Estou sanitizando nomes de arquivo
-
BUNNY_LIBRARY_IDé o ID numérico da Video Library -
BUNNY_API_KEYé a API Key da conta (não Storage Key) - Estou usando a URL correta:
https://video.bunnycdn.com - Aguardo processamento antes de pegar thumbnail
- Testei upload com arquivo pequeno
- Testei com nome de arquivo com espaços
- Testei delete
- Verifiquei logs de erro
- Confirmei que arquivo aparece no painel Bunny
<?php<?php<?phpAo deletar arquivos/vídeos no controller, SEMPRE valide se o caminho é uma string:
// ❌ ERRADO (pode causar TypeError)
$this->bunnyService->deleteFile($download['file_path']);
// ✅ CORRETO (com validação)
if (!empty($download['file_path'])) {
$filePath = $download['file_path'];
// Garantir que é string
if (!is_string($filePath)) {
error_log("Delete Error: file_path is not string - " . gettype($filePath));
throw new \Exception('Caminho do arquivo inválido');
}
$this->bunnyService->deleteFile($filePath);
}Por quê?
- Se
file_pathfor NULL, causa erro - Se o array inteiro for passado, causa TypeError
- Validação previne erros em produção
❌ {"HttpCode":401,"Message":"Unauthorized"}
Causas:
BUNNY_STORAGE_ZONEestá com ID numérico ao invés do nomeBUNNY_STORAGE_KEYincorreta- Usando API Key geral ao invés do Storage Key
Solução:
- Verifique se
BUNNY_STORAGE_ZONEé o NOME (ex: "meusite") - Pegue o Storage Key correto em: Storage Zone → FTP & API Access → Password
❌ {"HttpCode":404,"Message":"Not Found"}
Causas:
- Storage Zone não existe
- Nome da zona incorreto
- Região incorreta na URL
Solução:
- Confirme o nome exato da Storage Zone no painel
- Verifique a região (br, ny, uk, etc.)
❌ URL rejected: Malformed input to a URL function
Causa:
- Nome do arquivo tem espaços, parênteses ou caracteres especiais
Solução:
- SEMPRE sanitizar o nome do arquivo antes de fazer upload
- Use a função
sanitizeFileName()mostrada acima
❌ HTTP Code: 0
Causas:
- Firewall bloqueando
- DNS não resolvendo
- Timeout de conexão
- URL mal formada
Solução:
- Teste a URL manualmente:
curl -I https://br.storage.bunnycdn.com - Verifique se o servidor permite conexões externas
- Aumente o timeout do cURL
❌ Domain suspended or not configured
Causas:
- Vídeo ainda está sendo processado
- Pull Zone não configurada
- URL da thumbnail incorreta
Solução:
- Aguarde 2-5 minutos após upload
- Verifique status do vídeo (deve ser 4 = pronto)
- Use proxy para thumbnails se necessário
❌ Argument #1 ($remotePath) must be of type string, null given
❌ Argument #1 ($remotePath) must be of type string, array given
Causa:
file_pathouvideo_guidestá NULL no banco- Passando array inteiro ao invés de string
- Campo não existe no resultado da query
- Campo
file_pathnão foi salvo no INSERT
Solução:
// 1. Garantir que file_path é salvo no banco
$downloadId = $this->downloadModel->create([
'title' => $title,
'description' => $description,
'file_name' => $fileName,
'file_path' => $bunnyPath, // ✅ IMPORTANTE!
'file_url' => $fileUrl,
'file_size' => $fileSize
]);
// 2. Sempre validar antes de deletar
if (!empty($item['file_path']) && is_string($item['file_path'])) {
$this->bunnyService->deleteFile($item['file_path']);
}remotePath (caminho do arquivo no Bunny):
// Ao fazer upload, salve o caminho:
$remotePath = 'downloads/1234_arquivo.pdf';
$bunnyService->uploadFile($localFile, $remotePath);
// Depois, use esse mesmo caminho para deletar:
$bunnyService->deleteFile($remotePath);- Tenho acesso ao painel Bunny.net
- Sei qual serviço vou usar (Storage ou Stream)
- Tenho as credenciais corretas
-
BUNNY_STORAGE_ZONEé o NOME (não ID) -
BUNNY_STORAGE_KEYé o Password da Storage Zone - Sei a região da Storage Zone (br, ny, uk, etc.)
- URL usa a região correta:
https://{region}.storage.bunnycdn.com - Estou usando
CURLOPT_PUT+CURLOPT_INFILE - Estou sanitizando nomes de arquivo
-
BUNNY_LIBRARY_IDé o ID numérico da Video Library -
BUNNY_API_KEYé a API Key da conta (não Storage Key) - Estou usando a URL correta:
https://video.bunnycdn.com - Aguardo processamento antes de pegar thumbnail
- Testei upload com arquivo pequeno
- Testei com nome de arquivo com espaços
- Testei delete
- Verifiquei logs de erro
- Confirmei que arquivo aparece no painel Bunny
<?php
// test-bunny-storage.php
require_once __DIR__ . '/vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
$storageZone = $_ENV['BUNNY_STORAGE_ZONE'];
$storageKey = $_ENV['BUNNY_STORAGE_KEY'];
$region = 'br'; // Ajustar conforme sua região
// Criar arquivo de teste
$testFile = sys_get_temp_dir() . '/bunny_test.txt';
file_put_contents($testFile, "Teste - " . date('Y-m-d H:i:s'));
// Upload
$remotePath = 'test/' . time() . '.txt';
$url = "https://{$region}.storage.bunnycdn.com/{$storageZone}/{$remotePath}";
$fileHandle = fopen($testFile, 'r');
$fileSize = filesize($testFile);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, $fileHandle);
curl_setopt($ch, CURLOPT_INFILESIZE, $fileSize);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'AccessKey: ' . $storageKey,
'Content-Type: application/octet-stream'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
fclose($fileHandle);
echo "HTTP Code: {$httpCode}\n";
echo "Response: {$response}\n";
echo "URL: {$url}\n";
if ($httpCode === 201) {
echo "✅ SUCESSO!\n";
} else {
echo "❌ FALHOU!\n";
}
unlink($testFile);<?php
// check-env.php
require_once __DIR__ . '/vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
echo "=== BUNNY STORAGE ===\n";
echo "BUNNY_STORAGE_ZONE: " . ($_ENV['BUNNY_STORAGE_ZONE'] ?? '❌ NÃO DEFINIDO') . "\n";
echo "BUNNY_STORAGE_KEY: " . (isset($_ENV['BUNNY_STORAGE_KEY']) ? '✅ DEFINIDO' : '❌ NÃO DEFINIDO') . "\n";
echo "BUNNY_CDN_URL: " . ($_ENV['BUNNY_CDN_URL'] ?? '❌ NÃO DEFINIDO') . "\n\n";
echo "=== BUNNY STREAM ===\n";
echo "BUNNY_LIBRARY_ID: " . ($_ENV['BUNNY_LIBRARY_ID'] ?? '❌ NÃO DEFINIDO') . "\n";
echo "BUNNY_API_KEY: " . (isset($_ENV['BUNNY_API_KEY']) ? '✅ DEFINIDO' : '❌ NÃO DEFINIDO') . "\n";<?php
// debug-upload.php
error_reporting(E_ALL);
ini_set('display_errors', 1);
require_once __DIR__ . '/vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
$storageZone = $_ENV['BUNNY_STORAGE_ZONE'];
$storageKey = $_ENV['BUNNY_STORAGE_KEY'];
$region = 'br';
echo "Storage Zone: {$storageZone}\n";
echo "Storage Key: " . substr($storageKey, 0, 10) . "...\n";
echo "Region: {$region}\n\n";
// Criar arquivo
$testFile = sys_get_temp_dir() . '/test.txt';
file_put_contents($testFile, "Test content");
echo "File: {$testFile}\n";
echo "Exists: " . (file_exists($testFile) ? 'YES' : 'NO') . "\n";
echo "Size: " . filesize($testFile) . " bytes\n\n";
// Upload
$remotePath = 'debug/' . time() . '.txt';
$url = "https://{$region}.storage.bunnycdn.com/{$storageZone}/{$remotePath}";
echo "URL: {$url}\n\n";
$fileHandle = fopen($testFile, 'r');
$fileSize = filesize($testFile);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, $fileHandle);
curl_setopt($ch, CURLOPT_INFILESIZE, $fileSize);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'AccessKey: ' . $storageKey,
'Content-Type: application/octet-stream'
]);
curl_setopt($ch, CURLOPT_VERBOSE, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
$info = curl_getinfo($ch);
curl_close($ch);
fclose($fileHandle);
echo "HTTP Code: {$httpCode}\n";
echo "Response: {$response}\n";
echo "Error: " . ($error ?: 'None') . "\n";
echo "Upload Time: {$info['total_time']}s\n";
echo "Upload Size: {$info['size_upload']} bytes\n\n";
if ($httpCode === 201) {
echo "✅ SUCCESS!\n";
} else {
echo "❌ FAILED!\n";
}
unlink($testFile);- Storage API: https://docs.bunny.net/reference/storage-api
- Stream API: https://docs.bunny.net/reference/video-api
- Upload Storage: https://docs.bunny.net/reference/put_-storagezonename-path-filename
- Upload Stream: https://docs.bunny.net/reference/post_library-libraryid-videos
- Dashboard: https://dash.bunny.net
- Storage Zones: https://dash.bunny.net/storage
- Video Libraries: https://dash.bunny.net/stream
- Account Settings: https://dash.bunny.net/account/settings
-
Pergunte:
- Qual serviço? (Storage ou Stream)
- Qual região? (BR, NY, UK, etc.)
- Já tem credenciais?
-
Configure .env:
- Storage:
BUNNY_STORAGE_ZONE(NOME!),BUNNY_STORAGE_KEY,BUNNY_CDN_URL - Stream:
BUNNY_LIBRARY_ID,BUNNY_API_KEY
- Storage:
-
Implemente:
- Use
CURLOPT_PUT+CURLOPT_INFILE(nãoPOSTFIELDS) - Sanitize nomes de arquivo
- Use URL com região correta
- Use
-
Teste:
- Crie script de debug
- Teste com arquivo pequeno
- Verifique logs
-
Documente:
- Adicione comentários no código
- Crie README se necessário
- Liste credenciais necessárias
- ❌ Usar ID numérico em
BUNNY_STORAGE_ZONE - ❌ Usar
CURLOPT_POSTFIELDSpara upload - ❌ Esquecer de sanitizar nomes de arquivo
- ❌ Confundir Storage Key com API Key
- ❌ Esquecer a região na URL
- ✅ Verificar se é NOME da Storage Zone
- ✅ Usar
CURLOPT_PUT+CURLOPT_INFILE - ✅ Sanitizar nomes de arquivo
- ✅ Incluir região na URL
- ✅ Verificar HTTP Code (201 = sucesso)
- ✅ Adicionar logs de erro detalhados
- ✅ Criar script de teste
- Versão inicial
- Baseado em implementação real e debugada
- Inclui todos os erros encontrados e soluções
- Scripts de teste incluídos
Desenvolvido por Dante Testa
Data: 2025-01-13 11:35:00