Upload de Arquivos em PHP
Aprenda a realizar uploads de arquivos em PHP com $_FILES, move_uploaded_file() e validação segura de tipo e tamanho.
O upload de arquivos é um requisito comum no desenvolvimento web. Seja uma foto de perfil, um documento PDF ou uma importação CSV, permitir que os usuários enviem arquivos para o servidor é uma funcionalidade essencial na maioria das aplicações. Em PHP, isso é feito com a função move_uploaded_file() em conjunto com o array superglobal $_FILES.
Este capítulo cobre todo o fluxo de upload: configurar o formulário HTML, ler os metadados do arquivo enviado a partir de $_FILES, validá-los com segurança e mover o arquivo para seu destino final. Também aborda os códigos de erro de upload e as configurações do PHP que controlam os limites de upload.
Como funciona o upload de arquivos em PHP
O upload de um arquivo ocorre em três etapas:
- O navegador envia o arquivo em uma requisição POST do tipo
multipart/form-data. - O PHP recebe o arquivo e o grava em um local temporário no disco, expondo os detalhes sobre ele no array
$_FILES. - O script valida o arquivo e o move do local temporário para um local permanente com
move_uploaded_file().
O arquivo temporário é excluído automaticamente ao final da requisição se você não o mover, portanto, é necessário processá-lo durante a mesma requisição.
O superglobal $_FILES
Quando um arquivo é enviado, as informações sobre ele são armazenadas no array superglobal $_FILES. Para um campo de formulário chamado userfile, o array contém as seguintes chaves:
$_FILES['userfile']['name']- O nome original do arquivo enviado.$_FILES['userfile']['type']- O tipo MIME do arquivo enviado.$_FILES['userfile']['size']- O tamanho do arquivo enviado em bytes.$_FILES['userfile']['tmp_name']- O local temporário do arquivo enviado no servidor.$_FILES['userfile']['error']- Um código de erro indicando se houve algum problema durante o upload (veja os códigos de erro abaixo).
Passo 1: O formulário HTML
O atributo enctype do formulário deve ser definido como multipart/form-data, e o método deve ser POST. Sem multipart/form-data, o navegador envia apenas o nome do arquivo, não o seu conteúdo, e $_FILES ficará vazio.
<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="file" name="userfile">
<input type="submit" value="Upload">
</form>Passo 2: Validar o upload
Nunca confie em um arquivo enviado. Antes de movê-lo, verifique três coisas: se o upload foi bem-sucedido, se o tamanho está dentro do limite definido e se o arquivo realmente é do tipo esperado.
Verificar o código de erro
Sempre inspecione $_FILES['userfile']['error'] primeiro. O PHP define constantes nomeadas para os possíveis valores:
| Constante | Valor | Significado |
|---|---|---|
UPLOAD_ERR_OK | 0 | Sem erros, o arquivo foi enviado com sucesso. |
UPLOAD_ERR_INI_SIZE | 1 | O arquivo excede upload_max_filesize no php.ini. |
UPLOAD_ERR_FORM_SIZE | 2 | O arquivo excede o campo MAX_FILE_SIZE do formulário. |
UPLOAD_ERR_PARTIAL | 3 | O arquivo foi enviado apenas parcialmente. |
UPLOAD_ERR_NO_FILE | 4 | Nenhum arquivo foi enviado. |
UPLOAD_ERR_NO_TMP_DIR | 6 | Pasta temporária ausente. |
UPLOAD_ERR_CANT_WRITE | 7 | Falha ao gravar o arquivo no disco. |
Validar tipo e tamanho com segurança
Não confie em $_FILES['userfile']['type']. Esse valor é fornecido pelo navegador e pode ser facilmente falsificado por um atacante. Em vez disso, detecte o tipo MIME real a partir do conteúdo do arquivo com a extensão finfo e aplique um limite de tamanho você mesmo:
$file = $_FILES['userfile'];
// 1. Did the upload succeed?
if ($file['error'] !== UPLOAD_ERR_OK) {
exit("Upload failed with error code " . $file['error']);
}
// 2. Enforce a maximum size (2 MB here).
$maxBytes = 2 * 1024 * 1024;
if ($file['size'] > $maxBytes) {
exit("File is too large.");
}
// 3. Detect the real MIME type, not the client-supplied one.
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
$allowed = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/gif' => 'gif',
];
if (!isset($allowed[$mime])) {
exit("Only JPEG, PNG, and GIF images are allowed.");
}Passo 3: Mover o arquivo para seu destino final
Após validar o arquivo, mova-o com move_uploaded_file(). Essa função recebe dois argumentos: o caminho temporário ($_FILES['userfile']['tmp_name']) e o caminho de destino. Usar essa função em vez de copy() ou rename() é importante — ela verifica se o arquivo foi realmente enviado via HTTP, bloqueando um atacante de enganar o script para mover um arquivo arbitrário do servidor.
Gere o nome final do arquivo você mesmo, em vez de confiar no nome original. Isso evita ataques de travessia de diretório (como um nome ../../config.php) e a sobrescrita de arquivos existentes:
$targetDir = "uploads/";
// Build a safe, unique file name; never trust the client's name.
$extension = $allowed[$mime];
$safeName = bin2hex(random_bytes(8)) . "." . $extension;
$targetFile = $targetDir . $safeName;
if (move_uploaded_file($file['tmp_name'], $targetFile)) {
echo "The file was uploaded as " . $safeName;
} else {
echo "There was an error saving the file.";
}Para mais detalhes sobre essa função e sua companheira is_uploaded_file(), consulte os capítulos de referência move_uploaded_file() e is_uploaded_file().
Configurações que afetam os uploads
Algumas diretivas do php.ini limitam silenciosamente o que o script pode receber. Se os uploads falharem para arquivos grandes mesmo com o código correto, verifique estas configurações:
file_uploads— deve estarOnpara que os uploads funcionem.upload_max_filesize— o maior arquivo individual que o PHP aceitará (padrão2M).post_max_size— o maior corpo de POST total; deve ser maior queupload_max_filesize.max_file_uploads— o número máximo de arquivos em uma única requisição.
Conclusão
O upload de arquivos em PHP é uma parte fundamental do desenvolvimento web. Com o array superglobal $_FILES e a função move_uploaded_file(), você pode lidar com uploads em poucas linhas. A parte difícil é fazê-lo com segurança: sempre verifique o código de erro do upload, aplique um limite de tamanho, detecte o tipo MIME real com finfo em vez de confiar em $_FILES[...]['type'] e gere seu próprio nome de arquivo para que o cliente nunca controle onde o arquivo será armazenado. Para processar o arquivo salvo posteriormente, consulte manipulação de arquivos PHP e validação de formulários.