Em sistemas de gerenciamento de arquivos, a capacidade de manipular múltiplos itens simultaneamente é crucial para a usabilidade. Enquanto operações unitárias de CRUD são a base, a eficiência real é alcançada através do processamento em lote. Este artigo detalha a arquitetura necessária para implementar seleção múltipla, exclusão em massa e a funcionalidade de compressão e download de arquivos via ZIP utilizando PHP e JavaScript.
Arquitetura de Processamento em Lote
A transição de operações singulares para operações em massa requer uma coordenação específica entre o cliente e o servidor. O fluxo de dados pode ser resumido em três etapas:
- Coleta de Dados (Front-end): A interface deve fornecer mecanismos de seleção (geralmente checkboxes). O JavaScript monitora o estado desses elementos e compila uma lista de identificadores ou caminhos de arquivo.
- Envio da Carga (Payload): Os dados coletados são enviados ao servidor. Embora seja possível usar parâmetros de URL (GET), o método POST é preferencial para listas maiores ou para operações que alteram o estado do servidor, como exclusões.
- Processamento Centralizado (Back-end): O servidor recebe um array de caminhos. Para operações simples como exclusão, utiliza-se laços de repetição com funções de sistema de arquivos. Para downloads, utiliza-se a classe
ZipArchivepara consolidar os arquivos antes da transmissão.
Implementação do Front-end: Coleta e Envio
No lado do cliente, o objetivo é transformar interações de UI em uma estrutura de dados enviável. O código a seguir substitui a abordagem anterior por uma lógica mais modular, utilizando a API moderna do DOM e manipulação de arrays para preparar o envio.
// Seleção de elementos
const fileInputs = document.querySelectorAll('.file-selection-checkbox');
const batchDownloadBtn = document.getElementById('action-download-zip');
const batchDeleteBtn = document.getElementById('action-delete-batch');
// Controle de estado dos botões
function updateBatchControls() {
const hasSelection = Array.from(fileInputs).some(input => input.checked);
[batchDownloadBtn, batchDeleteBtn].forEach(btn => {
btn.disabled = !hasSelection;
btn.classList.toggle('opacity-50', !hasSelection);
btn.classList.toggle('cursor-not-allowed', !hasSelection);
});
}
// Adicionar ouvintes de eventos
fileInputs.forEach(input => input.addEventListener('change', updateBatchControls));
// Lógica de Download em Lote
batchDownloadBtn.addEventListener('click', () => {
const selectedPaths = Array.from(fileInputs)
.filter(input => input.checked)
.map(input => input.dataset.location);
if (selectedPaths.length === 0) return;
// Construção de um formulário dinâmico para envio POST seguro
const form = document.createElement('form');
form.method = 'POST';
form.action = 'index.php';
const actionInput = document.createElement('input');
actionInput.type = 'hidden';
actionInput.name = 'operation_type';
actionInput.value = 'zip_download';
form.appendChild(actionInput);
selectedPaths.forEach(path => {
const pathInput = document.createElement('input');
pathInput.type = 'hidden';
pathInput.name = 'target_files[]';
pathInput.value = path;
form.appendChild(pathInput);
});
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
});
Implementação do Back-end: Geração e Transmissão de ZIP
O back-end PHP atua como o orquestrador. Ao receber a requisição, ele instancia um objeto ZipArchive. O processo envolve criar um arquivo temporário no servidor, preenchê-lo com os itens solicitados e, crucialmente, limpar este arquivo temporário após o download para evitar o esgotamento do espaço em disco.
// Arquivo: index.php
// Configuração do diretório de upload
define('STORAGE_PATH', __DIR__ . '/uploads');
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['operation_type'])) {
if ($_POST['operation_type'] === 'zip_download' && !empty($_POST['target_files'])) {
$fileList = $_POST['target_files'];
$archiveObj = new ZipArchive();
// Gerar nome único para o arquivo temporário
$tempArchiveName = 'arquivo_' . time() . '_' . bin2hex(random_bytes(4)) . '.zip';
$tempArchivePath = sys_get_temp_dir() . '/' . $tempArchiveName;
// Tentativa de criação do arquivo ZIP
if ($archiveObj->open($tempArchivePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
foreach ($fileList as $relativePath) {
// Sanitização e validação do caminho
$fullPath = STORAGE_PATH . '/' . $relativePath;
if (file_exists($fullPath) && is_file($fullPath)) {
// Adiciona o arquivo ao ZIP, renomeando apenas para o basename para simplificar
$archiveObj->addFile($fullPath, basename($fullPath));
}
}
// Fecha o arquivo para salvar as alterações
$archiveObj->close();
// Verifica se o arquivo foi criado com sucesso antes de enviar
if (file_exists($tempArchivePath)) {
// Configuração dos Cabeçalhos HTTP para forçar o download
header('Content-Description: Transferência de Arquivo');
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . $tempArchiveName . '"');
header('Content-Length: ' . filesize($tempArchivePath));
header('Cache-Control: public, must-revalidate');
header('Pragma: no-cache');
// Limpeza do buffer de saída
ob_clean();
flush();
// Leitura e envio do arquivo
readfile($tempArchivePath);
// Remoção do arquivo temporário
unlink($tempArchivePath);
exit;
}
}
}
// Lógica para exclusão em massa seria implementada aqui, usando unlink() em loop
}
Detalhes Técnicos do Processo de Compressão
A classe ZipArchive é a ferramenta padrão do PHP para esta tarefa. O método open aceita flags que determinam o comportamento: ZipArchive::CREATE cria o arquivo se ele não existir, enquanto ZipArchive::OVERWRITE garante que estamos começando com um arquivo limpo, o que é vital para evitar corrupção de dados em requisições concorrentes. A função addFile é responsável por ler o arquivo do disco e injetá-lo no arquivo compactado. É importante notar que o segundo parâmetro de addFile define o nome interno do arquivo dentro do ZIP, permitindo reestruturar a organização dos arquivos durante o empacotamento.
Por fim, a função header é essnecial. Sem definir o Content-Type corretamente como application/zip e o Content-Disposition como attachment, o navegador pode tentar exibir o conteúdo binário como texto ou renderizá-lo na própria janela, falhando a tentativa de download. A remoção do arquivo temporário via unlink é o passo final de "higiene do sistema", garantindo que o servidor não acumule resíduos de processamentos anteriores.