Pular para conteúdo

AWS S3 COPY - Copiar Arquivo entre Buckets/Pastas

O que é este Node?

O AWS S3 COPY é o node responsável por copiar arquivos entre buckets S3 ou entre pastas dentro do mesmo bucket sem precisar baixar e fazer upload novamente.

Por que este Node existe?

Copiar arquivos no S3 de forma eficiente é crucial. O S3 COPY existe para:

  1. Backup entre buckets: Copiar de produção para backup
  2. Organização: Mover arquivos entre pastas (copy + delete)
  3. Replicação manual: Duplicar arquivos para outras regiões
  4. Versionamento manual: Criar cópias com novos nomes
  5. Eficiência: Copiar server-side (sem download/upload)

Como funciona internamente?

Quando o S3 COPY é executado, o sistema:

  1. Recebe credenciais AWS: Access Key ID, Secret Access Key e região
  2. Localiza arquivo origem: sourceBucket + sourceKey
  3. Define destino: destinationBucket + destinationKey
  4. Copia server-side: S3 copia internamente (sem tráfego)
  5. Preserva metadados: ContentType, metadata original
  6. Retorna confirmação: CopyResult com metadados

Código interno (aws-executors.service.ts:71-80):

case 'copy':
  const copyResult = await s3.copyObject({
    Bucket: data.destinationBucket,
    CopySource: `${data.sourceBucket}/${data.sourceKey}`,
    Key: data.destinationKey,
  }).promise();
  return {
    success: true,
    copyResult,
  };

Quando você DEVE usar este Node?

Use S3 COPY sempre que precisar duplicar arquivos no S3:

Casos de uso

  1. Backup manual: Copiar arquivo importante para bucket de backup
  2. Reorganização: Mover arquivo de temp/ para permanente/
  3. Duplicação para processamento: Criar cópia antes de modificar
  4. Migração entre regiões: Copiar de us-east-1 para sa-east-1
  5. Versionamento: Criar backup-YYYY-MM-DD.json

Quando NÃO usar S3 COPY

  • Replicação automática: Use S3 Replication Rules
  • Arquivos gigantes (>5GB): Use Multipart Copy
  • Cross-account: Precisa permissões especiais

Parâmetros Detalhados

accessKeyId (string, obrigatório)

O que é: Sua AWS Access Key ID (credencial de autenticação).

Onde conseguir: IAM Console → Users → Security credentials → Create access key

Flow completo para testar:

{
  "name": "Teste S3 Copy - Access Key",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "s3_1",
      "type": "aws_s3",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Copiar S3",
        "parameters": {
          "operation": "copy",
          "accessKeyId": "AKIAIOSFODNN7EXAMPLE",
          "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
          "region": "us-east-1",
          "sourceBucket": "bucket-origem",
          "sourceKey": "documentos/original.pdf",
          "destinationBucket": "bucket-destino",
          "destinationKey": "backup/copia.pdf"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "Arquivo copiado com sucesso!"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "s3_1" },
    { "source": "s3_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Substitua pelos seus dados reais. Arquivo será copiado server-side.

secretAccessKey (string, obrigatório)

O que é: Sua AWS Secret Access Key (senha da credencial).

Segurança: NUNCA exponha essa chave! Use variáveis de ambiente.

region (string, obrigatório)

O que é: Região AWS onde estão os buckets.

IMPORTANTE: Para copy cross-region, use região do bucket de DESTINO.

Valores comuns: - us-east-1 (Norte da Virgínia) - us-west-2 (Oregon) - sa-east-1 (São Paulo) - eu-west-1 (Irlanda)

Flow completo para testar:

{
  "name": "Teste S3 Copy - Cross Region",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "s3_1",
      "type": "aws_s3",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Copiar USA → Brasil",
        "parameters": {
          "operation": "copy",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "sa-east-1",
          "sourceBucket": "bucket-usa",
          "sourceKey": "data/arquivo.json",
          "destinationBucket": "bucket-brasil",
          "destinationKey": "data/arquivo.json"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Sucesso",
        "parameters": {
          "message": "Arquivo replicado de USA para Brasil!"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "s3_1" },
    { "source": "s3_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

sourceBucket (string, obrigatório)

O que é: Nome do bucket S3 onde está o arquivo original.

Flow completo para testar:

{
  "name": "Teste S3 Copy - Source Bucket",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "s3_1",
      "type": "aws_s3",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Backup",
        "parameters": {
          "operation": "copy",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "sourceBucket": "uploads-producao",
          "sourceKey": "importante/arquivo.pdf",
          "destinationBucket": "backups",
          "destinationKey": "importante/arquivo-backup.pdf"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "Backup criado em bucket 'backups'!"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "s3_1" },
    { "source": "s3_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

sourceKey (string, obrigatório)

O que é: Caminho completo do arquivo original no bucket origem.

Formato: pasta/subpasta/arquivo.ext

Flow completo para testar:

{
  "name": "Teste S3 Copy - Source Key Dinâmica",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Pedir Arquivo",
        "parameters": {
          "message": "Digite o nome do arquivo a copiar:",
          "variable": "nome_arquivo"
        }
      }
    },
    {
      "id": "s3_1",
      "type": "aws_s3",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Copiar",
        "parameters": {
          "operation": "copy",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "sourceBucket": "uploads",
          "sourceKey": "temp/{{nome_arquivo}}",
          "destinationBucket": "uploads",
          "destinationKey": "permanente/{{nome_arquivo}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Sucesso",
        "parameters": {
          "message": "{{nome_arquivo}} movido de temp/ para permanente/"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "s3_1" },
    { "source": "s3_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

destinationBucket (string, obrigatório)

O que é: Nome do bucket S3 de destino (pode ser o mesmo que origem).

Flow completo para testar:

{
  "name": "Teste S3 Copy - Mesmo Bucket",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "s3_1",
      "type": "aws_s3",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Duplicar no Mesmo Bucket",
        "parameters": {
          "operation": "copy",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "sourceBucket": "meu-bucket",
          "sourceKey": "original/arquivo.pdf",
          "destinationBucket": "meu-bucket",
          "destinationKey": "backup/arquivo-2025-01-15.pdf"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "Cópia criada em backup/ com data!"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "s3_1" },
    { "source": "s3_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

destinationKey (string, obrigatório)

O que é: Caminho completo do arquivo de destino.

IMPORTANTE: Se já existir, será sobrescrito!

Flow completo para testar:

{
  "name": "Teste S3 Copy - Destination Key com Data",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Gerar Nome Backup",
        "parameters": {
          "variable": "backup_nome",
          "value": "backup-2025-01-15-{{timestamp}}.json"
        }
      }
    },
    {
      "id": "s3_1",
      "type": "aws_s3",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Criar Backup Datado",
        "parameters": {
          "operation": "copy",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "sourceBucket": "dados",
          "sourceKey": "atual/dados.json",
          "destinationBucket": "dados",
          "destinationKey": "backups/{{backup_nome}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Sucesso",
        "parameters": {
          "message": "Backup criado: {{backup_nome}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "s3_1" },
    { "source": "s3_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Parâmetros

Campo Tipo Obrigatório Descrição
operation string Sim Deve ser "copy"
accessKeyId string Sim AWS Access Key ID
secretAccessKey string Sim AWS Secret Access Key
region string Sim Região AWS (do bucket destino)
sourceBucket string Sim Bucket origem
sourceKey string Sim Caminho do arquivo origem
destinationBucket string Sim Bucket destino
destinationKey string Sim Caminho do arquivo destino

Exemplo 1: Backup Diário Automatizado

Objetivo: Criar backup diário de arquivo crítico com data no nome.

JSON para Importar

{
  "name": "Backup Diário Automatizado",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Data Atual",
        "parameters": {
          "variable": "data_hoje",
          "value": "2025-01-15"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Iniciando",
        "parameters": {
          "message": "Iniciando backup diário de {{data_hoje}}..."
        }
      }
    },
    {
      "id": "s3_1",
      "type": "aws_s3",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Copiar Banco de Dados",
        "parameters": {
          "operation": "copy",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "sa-east-1",
          "sourceBucket": "producao-dados",
          "sourceKey": "database/production.db",
          "destinationBucket": "backups-diarios",
          "destinationKey": "database/backup-{{data_hoje}}.db"
        }
      }
    },
    {
      "id": "s3_2",
      "type": "aws_s3",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Copiar Configurações",
        "parameters": {
          "operation": "copy",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "sa-east-1",
          "sourceBucket": "producao-dados",
          "sourceKey": "config/app.config.json",
          "destinationBucket": "backups-diarios",
          "destinationKey": "config/config-{{data_hoje}}.json"
        }
      }
    },
    {
      "id": "message_2",
      "type": "message",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Concluído",
        "parameters": {
          "message": "Backup de {{data_hoje}} CONCLUÍDO!\n\n- Database: backup-{{data_hoje}}.db\n- Config: config-{{data_hoje}}.json"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1300, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "message_1" },
    { "source": "message_1", "target": "s3_1" },
    { "source": "s3_1", "target": "s3_2" },
    { "source": "s3_2", "target": "message_2" },
    { "source": "message_2", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Iniciando backup diário de 2025-01-15...
Sistema: Backup de 2025-01-15 CONCLUÍDO!

- Database: backup-2025-01-15.db
- Config: config-2025-01-15.json

Exemplo 2: Mover Arquivo (Copy + Delete)

Objetivo: Mover arquivo de pasta temporária para permanente.

JSON para Importar

{
  "name": "Mover Arquivo - Temp para Permanente",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "ID Documento",
        "parameters": {
          "message": "Digite o ID do documento processado:",
          "variable": "doc_id"
        }
      }
    },
    {
      "id": "s3_copy_1",
      "type": "aws_s3",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Copiar para Permanente",
        "parameters": {
          "operation": "copy",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "sourceBucket": "uploads",
          "sourceKey": "temp/doc-{{doc_id}}.pdf",
          "destinationBucket": "uploads",
          "destinationKey": "permanente/doc-{{doc_id}}.pdf"
        }
      }
    },
    {
      "id": "s3_delete_1",
      "type": "aws_s3",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Deletar Temporário",
        "parameters": {
          "operation": "delete",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "bucketName": "uploads",
          "key": "temp/doc-{{doc_id}}.pdf"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Concluído",
        "parameters": {
          "message": "Documento {{doc_id}} MOVIDO com sucesso!\n\nDe: temp/\nPara: permanente/"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1100, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "s3_copy_1" },
    { "source": "s3_copy_1", "target": "s3_delete_1" },
    { "source": "s3_delete_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Digite o ID do documento processado:
Usuário: 123
Sistema: Documento 123 MOVIDO com sucesso!

De: temp/
Para: permanente/

Resposta do Node

{
  "success": true,
  "copyResult": {
    "CopyObjectResult": {
      "ETag": "\"abc123def456\"",
      "LastModified": "2025-01-15T10:30:00.000Z"
    }
  }
}

Configuração AWS

Permissões IAM Necessárias

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::bucket-origem/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::bucket-destino/*"
    }
  ]
}

IMPORTANTE: Precisa GetObject no origem E PutObject no destino!

Boas Práticas

Performance

SIM: - Copy é server-side (muito mais rápido que download+upload) - Para arquivos >5GB, use Multipart Copy - Copy entre mesma região é instantâneo - Copy cross-region tem custo de transferência

NÃO: - Não use copy para arquivos >5GB sem multipart - Não faça copy em loop (use batch operations)

Segurança

SIM: - Verifique permissões em AMBOS os buckets - Para cross-account, configure bucket policy - Preserve ACL se necessário (use CopySource + ACL) - Valide que arquivo origem existe antes

NÃO: - Não sobrescreva arquivos importantes sem backup - Não copie entre accounts sem policy correta

Organização

SIM: - Use copy para reorganizar estrutura de pastas - Crie backups com data no nome - Implemente "move" com copy + delete atômico

Padrão de backup com data:

original/arquivo.pdf
backup/arquivo-2025-01-15.pdf
backup/arquivo-2025-01-16.pdf

Custos

SIM: - Copy na mesma região é grátis (só armazenamento) - Copy cross-region tem custo de transferência - Consider lifecycle policies para deletar backups antigos

NÃO: - Não mantenha infinitos backups (configure lifecycle) - Não copie cross-region sem necessidade

Dicas

Server-side: Copy é 100% server-side. Nenhum dado passa pelo seu servidor.

Preservar metadata: Por padrão, metadata é copiado. Use MetadataDirective para controlar.

Sobrescrever: Se destinationKey já existe, será sobrescrito SEM aviso!

Move: S3 não tem "move" nativo. Faça copy + delete.

Cross-region: Funciona, mas tem custo de transferência entre regiões.

Same key: Pode copiar para mesma key (sobrescreve). Útil para atualizar metadata.

Versionamento: Se destino tem versionamento, cria nova versão.

ETag: Use ETag retornado para verificar integridade.

Próximo Node

S3 UPLOAD - Enviar arquivo para S3 → S3 DOWNLOAD - Baixar arquivo do S3 → S3 DELETE - Deletar arquivo do S3 → S3 LIST - Listar objetos no bucket