Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save dantetesta/927c33a0ffd28add9c352d5dce900b89 to your computer and use it in GitHub Desktop.

Select an option

Save dantetesta/927c33a0ffd28add9c352d5dce900b89 to your computer and use it in GitHub Desktop.
Manual de Integração Bunny Stream e Bunny Storage

🐰 Guia Definitivo de Integração Bunny.net para IAs

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.


📋 Índice

  1. Bunny Storage (Arquivos/Downloads)
  2. Bunny Stream (Vídeos)
  3. Erros Comuns e Soluções
  4. Checklist de Verificação
  5. Scripts de Debug

🗂️ BUNNY STORAGE (Arquivos/Downloads)

1. Credenciais Necessárias

# .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)

⚠️ ATENÇÃO CRÍTICA:

  1. BUNNY_STORAGE_ZONE = NOME da zona (ex: "meusite"), NÃO o ID numérico (ex: 1256199)
  2. BUNNY_STORAGE_KEY = Password específico da Storage Zone (não é a API Key geral)
  3. Encontre em: https://dash.bunny.net/storage → Clique na zona → "FTP & API Access"

2. Determinar a Região

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"


3. Código de Upload (MÉTODO CORRETO)

❌ ERRADO (Não funciona):

// NÃO USE ISSO!
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, $fileContent);

✅ CORRETO (Conforme documentação oficial):

/**
 * 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;
}

4. Código de Delete

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;
}

5. Gerar URL do CDN

public function getStorageUrl(string $remotePath): string
{
    $cdnUrl = $_ENV['BUNNY_CDN_URL']; // Ex: https://meusite.b-cdn.net
    return rtrim($cdnUrl, '/') . '/' . ltrim($remotePath, '/');
}

6. ⚠️ SANITIZAR NOMES DE ARQUIVO (CRÍTICO!)

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).pdffolder11.pdf
  • Meu Arquivo (2023).pdfMeu_Arquivo_2023.pdf
  • Relatório - Março.docxRelatorio_-_Marco.docx

🎬 BUNNY STREAM (Vídeos)

1. Credenciais Necessárias

# .env
BUNNY_LIBRARY_ID=123456                # ID da Video Library
BUNNY_API_KEY=abc123-def456-...        # API Key da conta Bunny

Como encontrar:

  1. BUNNY_LIBRARY_ID:

  2. BUNNY_API_KEY:


2. Código de Upload de Vídeo

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;
}

3. Gerar URL do Player

public function getPlayerUrl(string $videoGuid): string
{
    $libraryId = $_ENV['BUNNY_LIBRARY_ID'];
    return "https://iframe.mediadelivery.net/embed/{$libraryId}/{$videoGuid}?autoplay=false&preload=true";
}

4. Obter Informações do Vídeo

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;
}

5. Gerar Thumbnail (Após Processamento)

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
}

6. Delete de Vídeo

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;
}

7. Atualizar Informações do Vídeo

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'
]);

8. Substituir Vídeo

Para substituir um vídeo, você precisa:

  1. Deletar o vídeo antigo
  2. 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');

⚠️ ATENÇÃO: Não há como sobrescrever um vídeo existente. Você deve deletar e criar novo.


✅ CHECKLIST DE VERIFICAÇÃO

Antes de Começar:

  • Tenho acesso ao painel Bunny.net
  • Sei qual serviço vou usar (Storage ou Stream)
  • Tenho as credenciais corretas

Para Bunny Storage:

  • 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

Para Bunny Stream:

  • 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

Após Implementar:

  • 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

🔬 SCRIPTS DE DEBUG

1. Testar Conexão e Upload

<?php

2. Testar Download

<?php

3. Testar Delete

<?php

⚠️ IMPORTANTE: Validação ao Deletar

Ao 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_path for NULL, causa erro
  • Se o array inteiro for passado, causa TypeError
  • Validação previne erros em produção

🐛 ERROS COMUNS E SOLUÇÕES

1. Erro 401 (Unauthorized)

❌ {"HttpCode":401,"Message":"Unauthorized"}

Causas:

  • BUNNY_STORAGE_ZONE está com ID numérico ao invés do nome
  • BUNNY_STORAGE_KEY incorreta
  • Usando API Key geral ao invés do Storage Key

Solução:

  1. Verifique se BUNNY_STORAGE_ZONE é o NOME (ex: "meusite")
  2. Pegue o Storage Key correto em: Storage Zone → FTP & API Access → Password

2. Erro 404 (Not Found)

❌ {"HttpCode":404,"Message":"Not Found"}

Causas:

  • Storage Zone não existe
  • Nome da zona incorreto
  • Região incorreta na URL

Solução:

  1. Confirme o nome exato da Storage Zone no painel
  2. Verifique a região (br, ny, uk, etc.)

3. URL Rejected / Malformed URL

❌ 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

4. HTTP 0 (Sem Resposta)

❌ HTTP Code: 0

Causas:

  • Firewall bloqueando
  • DNS não resolvendo
  • Timeout de conexão
  • URL mal formada

Solução:

  1. Teste a URL manualmente: curl -I https://br.storage.bunnycdn.com
  2. Verifique se o servidor permite conexões externas
  3. Aumente o timeout do cURL

5. Thumbnail Não Aparece

❌ Domain suspended or not configured

Causas:

  • Vídeo ainda está sendo processado
  • Pull Zone não configurada
  • URL da thumbnail incorreta

Solução:

  1. Aguarde 2-5 minutos após upload
  2. Verifique status do vídeo (deve ser 4 = pronto)
  3. Use proxy para thumbnails se necessário

6. TypeError ao Deletar

❌ Argument #1 ($remotePath) must be of type string, null given
❌ Argument #1 ($remotePath) must be of type string, array given

Causa:

  • file_path ou video_guid está NULL no banco
  • Passando array inteiro ao invés de string
  • Campo não existe no resultado da query
  • Campo file_path nã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']);
}

⚠️ IMPORTANTE: Para deletar funcionar, você precisa ter salvo o 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);

✅ CHECKLIST DE VERIFICAÇÃO

Antes de Começar:

  • Tenho acesso ao painel Bunny.net
  • Sei qual serviço vou usar (Storage ou Stream)
  • Tenho as credenciais corretas

Para Bunny Storage:

  • 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

Para Bunny Stream:

  • 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

Após Implementar:

  • 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

🔬 SCRIPTS DE DEBUG

1. Testar Conexão e Upload

<?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);

2. Verificar Variáveis de Ambiente

<?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";

3. Debug Completo com Logs

<?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);

📚 REFERÊNCIAS OFICIAIS

Documentação Bunny.net:

Painel Bunny.net:


🎯 RESUMO PARA IAs

Ao Receber Pedido de Integração:

  1. Pergunte:

    • Qual serviço? (Storage ou Stream)
    • Qual região? (BR, NY, UK, etc.)
    • Já tem credenciais?
  2. Configure .env:

    • Storage: BUNNY_STORAGE_ZONE (NOME!), BUNNY_STORAGE_KEY, BUNNY_CDN_URL
    • Stream: BUNNY_LIBRARY_ID, BUNNY_API_KEY
  3. Implemente:

    • Use CURLOPT_PUT + CURLOPT_INFILE (não POSTFIELDS)
    • Sanitize nomes de arquivo
    • Use URL com região correta
  4. Teste:

    • Crie script de debug
    • Teste com arquivo pequeno
    • Verifique logs
  5. Documente:

    • Adicione comentários no código
    • Crie README se necessário
    • Liste credenciais necessárias

⚠️ NUNCA FAÇA:

  • ❌ Usar ID numérico em BUNNY_STORAGE_ZONE
  • ❌ Usar CURLOPT_POSTFIELDS para upload
  • ❌ Esquecer de sanitizar nomes de arquivo
  • ❌ Confundir Storage Key com API Key
  • ❌ Esquecer a região na URL

✅ SEMPRE FAÇA:

  • ✅ 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

📝 CHANGELOG

v1.0 (2025-01-13)

  • 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment