Skip to main content
POST
/
media
/
upload
curl -X POST https://api.disparador.com/media/upload \
  -H "X-Access-Token: seu-access-token" \
  -F "file=@/caminho/para/arquivo.jpg"
{
  "url": "https://minio.disparador.com/company-123/media/2024/01/abc123def456-imagem.jpg",
  "filename": "imagem-produto.jpg",
  "size": 2458624,
  "type": "image/jpeg",
  "bucket": "company-123",
  "objectName": "media/2024/01/abc123def456-imagem.jpg"
}
Os arquivos são enviados para o MinIO (armazenamento de objetos) e retornam uma URL permanente para uso nas mensagens.

Tipos de Mídia Suportados

Imagens

  • JPEG/JPG (.jpg, .jpeg)
  • PNG (.png)
  • GIF (.gif)
  • WebP (.webp)
  • MIME types: image/*

Vídeos

  • MP4 (.mp4)
  • AVI (.avi)
  • MOV (.mov)
  • MKV (.mkv)
  • MIME types: video/*

Áudios

  • MP3 (.mp3)
  • WAV (.wav)
  • OGG (.ogg)
  • M4A (.m4a)
  • AAC (.aac)
  • MIME types: audio/*

Documentos

  • PDF (.pdf)
  • DOC/DOCX (.doc, .docx)
  • XLS/XLSX (.xls, .xlsx)
  • MIME types específicos aceitos

Headers

X-Access-Token
string
required
Token de acesso da empresa
Content-Type
string
required
Deve ser multipart/form-data (geralmente configurado automaticamente)

Body (Form Data)

file
file
required
Arquivo a ser enviado (campo do formulário)
curl -X POST https://api.disparador.com/media/upload \
  -H "X-Access-Token: seu-access-token" \
  -F "file=@/caminho/para/arquivo.jpg"

Response

url
string
URL pública permanente do arquivo no MinIO
filename
string
Nome original do arquivo
size
number
Tamanho do arquivo em bytes
type
string
Tipo MIME do arquivo
bucket
string
Nome do bucket no MinIO onde o arquivo foi armazenado
objectName
string
Nome do objeto no MinIO (caminho interno)
{
  "url": "https://minio.disparador.com/company-123/media/2024/01/abc123def456-imagem.jpg",
  "filename": "imagem-produto.jpg",
  "size": 2458624,
  "type": "image/jpeg",
  "bucket": "company-123",
  "objectName": "media/2024/01/abc123def456-imagem.jpg"
}

Upload com Progresso

function uploadWithProgress(file, accessToken, onProgress) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    const formData = new FormData();
    formData.append('file', file);
    
    // Monitorar progresso
    xhr.upload.addEventListener('progress', (e) => {
      if (e.lengthComputable) {
        const percentComplete = (e.loaded / e.total) * 100;
        onProgress(percentComplete);
      }
    });
    
    // Manipular resposta
    xhr.addEventListener('load', () => {
      if (xhr.status === 200) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject(new Error(`Upload falhou: ${xhr.status}`));
      }
    });
    
    xhr.addEventListener('error', () => {
      reject(new Error('Erro de rede no upload'));
    });
    
    xhr.open('POST', 'https://api.disparador.com/media/upload');
    xhr.setRequestHeader('X-Access-Token', accessToken);
    xhr.send(formData);
  });
}

// Uso
const progressBar = document.getElementById('progress');

uploadWithProgress(file, 'seu-token', (progress) => {
  console.log(`Upload: ${progress.toFixed(0)}%`);
  progressBar.style.width = `${progress}%`;
}).then(result => {
  console.log('Upload completo:', result);
}).catch(error => {
  console.error('Erro:', error);
});

Validação Prévia

function validateFile(file) {
  const MAX_SIZE = 100 * 1024 * 1024; // 100MB
  
  // Validações básicas
  const validations = {
    size: {
      valid: file.size <= MAX_SIZE,
      message: `Arquivo muito grande (máximo ${MAX_SIZE / 1024 / 1024}MB)`
    },
    type: {
      valid: isValidFileType(file.type),
      message: 'Tipo de arquivo não suportado'
    }
  };
  
  // Verificar todas as validações
  for (const [key, validation] of Object.entries(validations)) {
    if (!validation.valid) {
      throw new Error(validation.message);
    }
  }
  
  return true;
}

function isValidFileType(mimeType) {
  const allowedTypes = [
    'image/', 'video/', 'audio/',
    'application/pdf',
    'application/msword',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    'application/vnd.ms-excel',
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  ];
  
  return allowedTypes.some(type => mimeType.startsWith(type) || mimeType === type);
}

// Uso
try {
  validateFile(file);
  const result = await uploadMedia(file, accessToken);
} catch (error) {
  alert(error.message);
}

Upload em Lote

async function uploadMultipleFiles(files, accessToken) {
  const results = [];
  const errors = [];
  
  // Upload sequencial para evitar sobrecarga
  for (const file of files) {
    try {
      console.log(`Enviando ${file.name}...`);
      const result = await uploadMedia(file, accessToken);
      results.push({
        file: file.name,
        success: true,
        url: result.url
      });
    } catch (error) {
      errors.push({
        file: file.name,
        success: false,
        error: error.message
      });
    }
  }
  
  return { results, errors };
}

// Uso
const files = Array.from(document.getElementById('files').files);
const { results, errors } = await uploadMultipleFiles(files, 'seu-token');

console.log(`Sucesso: ${results.length}`);
console.log(`Erros: ${errors.length}`);

Integração com Campanhas

// Fazer upload e criar campanha com a mídia
async function createCampaignWithMedia(campaignData, mediaFile, accessToken) {
  // 1. Upload da mídia
  const mediaResult = await uploadMedia(mediaFile, accessToken);
  
  // 2. Determinar tipo de mídia
  const mediaType = mediaResult.type.split('/')[0]; // image, video, audio
  
  // 3. Criar campanha com a URL da mídia
  const campaign = {
    ...campaignData,
    mediaUrl: mediaResult.url,
    mediaType: mediaType,
    fileName: mediaResult.filename
  };
  
  const response = await fetch('https://api.disparador.com/api/campaigns', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Access-Token': accessToken
    },
    body: JSON.stringify(campaign)
  });
  
  return response.json();
}

Observações

Armazenamento MinIO:
  • Os arquivos são armazenados no MinIO (S3-compatible)
  • URLs são permanentes e públicas
  • Organização automática por empresa e data
  • Nomes de arquivo são únicos (UUID + nome original)
Limites e Segurança:
  • Validação de tipo MIME no servidor
  • Arquivos são organizados por empresa (isolamento)
  • Considere implementar limites de quota por empresa
  • URLs públicas - não envie arquivos sensíveis

Compatibilidade

O endpoint /media/files/{filename} ainda existe para compatibilidade com arquivos antigos armazenados localmente. Novos uploads são direcionados ao MinIO.
I