Resumo : neste tutorial, você aprenderá como criar um formulário de upload de arquivos e processar arquivos carregados com segurança em PHP.
Introdução ao elemento de entrada do arquivo
O <input>
elemento com type="file"
permite selecionar um ou mais arquivos de seu armazenamento e carregá-los no servidor por meio do envio do formulário .
O seguinte mostra o elemento de entrada do arquivo:
<input type="file" id="file" name="file">
Linguagem de código: HTML, XML ( xml )
O value
do <input>
elemento conterá o caminho para o arquivo selecionado. Para fazer upload de vários arquivos, você adiciona o multiple
atributo ao <input>
elemento assim:
<input type="file" id="file" name="file" multiple>
Linguagem de código: HTML, XML ( xml )
Neste caso, o value
atributo conterá o caminho do primeiro arquivo na lista de arquivos selecionada. Observe que você aprenderá como fazer upload de vários arquivos no próximo tutorial.
Para permitir o upload de determinados tipos de arquivo, você usa o accept
atributo. O valor do accept
atributo é um especificador de tipo de arquivo exclusivo, que pode ser:
- Uma extensão de nome de arquivo válida que não diferencia maiúsculas de minúsculas, por exemplo,
.jpg
,.pdf
,.txt
- Uma string de tipo MIME válida
- Ou uma string como
image/*
(qualquer arquivo de imagem),video/*
(qualquer arquivo de vídeo),audio/*
(qualquer arquivo de áudio).
Se você usar vários especificadores de tipo de arquivo, será necessário separá-los usando uma vírgula ( ,
). Por exemplo, a configuração a seguir permite fazer upload apenas .png
de .jpeg
imagens:
<input type="file" accept="image/png, image/jpeg" name="file">
Linguagem de código: HTML, XML ( xml )
O <form>
elemento que contém o elemento de entrada do arquivo deve ter o enctype
atributo com o valor multipart/form-data
:
<form enctype="multipart/form-data" action="index.php" method="post">
</form>
Linguagem de código: HTML, XML ( xml )
Caso contrário, o navegador não poderá fazer upload de arquivos.
O seguinte ilustra um formulário simples para fazer upload de um único arquivo por vez:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>PHP File Upload</title>
</head>
<body>
<form enctype="multipart/form-data" action="upload.php" method="post">
<div>
<label for="file">Select a file:</label>
<input type="file" id="file" name="file"/>
</div>
<div>
<button type="submit">Upload</button>
</div>
</form>
</body>
</html>
Linguagem de código: HTML, XML ( xml )
Configuração de upload de arquivo PHP
O PHP possui algumas opções importantes que controlam o upload do arquivo. Essas opções estão no php.ini
arquivo. Se você não sabe onde encontrar seu php.ini
arquivo, você pode usar a php_ini_loaded_file()
função da seguinte forma:
<?php
echo php_ini_loaded_file();
Linguagem de código: HTML, XML ( xml )
Ele retornará o seguinte caminho de arquivo se você usar o XAMPP no Windows:
C:\xampp\php\php.ini
Linguagem de código: CSS ( css )
Aqui estão as configurações importantes para uploads de arquivos no php.ini
arquivo:
; Whether to allow HTTP file uploads.
file_uploads=On
; Temporary directory for HTTP uploaded files (will use system default if not
; specified).
upload_tmp_dir="C:\xampp\tmp"
; Maximum allowed size for uploaded files.
upload_max_filesize=2M
; Maximum number of files that can be uploaded via a single request
max_file_uploads=20
Linguagem de código: texto simples ( texto simples )
uploads_de_arquivos
A file_upload
diretiva deveria ser On
permitir o upload de arquivos. O padrão é On
.
upload_max_filesize
Especifica upload_max_filesize
o tamanho máximo do arquivo carregado. Por padrão, é 2M (MB). Se você receber um erro informando que o arquivo excede upload_max_filesize
, será necessário aumentar esse valor.
upload_tmp_dir
Especifica o upload_tmp_dir
diretório que armazena os arquivos carregados temporariamente.
post_max_size
O post_max_size
especifica o tamanho máximo dos POST
dados. Como você fará upload de arquivos com a POST
solicitação, verifique se post_max_size
é maior que upload_max_size
.
max_file_uploads
A max_file_uploads
diretiva limita o número de arquivos que você pode enviar por vez.
Manipulando uploads de arquivos em PHP
Para acessar as informações de um arquivo carregado, você usa o $_FILES
array. Por exemplo, se o nome do elemento de entrada do arquivo for file
, você poderá acessar o arquivo carregado via $_FILES['file']
.
O $_FILE[‘file’] é um array associativo que consiste nas seguintes chaves:
name
: é o nome do arquivo enviado.type
: é o tipo MIME do arquivo de upload, por exemplo,image/jpeg
para imagem JPEG ouapplication/pdf
arquivo PDF.size
: é o tamanho do arquivo enviado em bytes.tmp_name
: é o arquivo temporário no servidor que armazenou o nome do arquivo carregado. Se o arquivo enviado for muito grande, o arquivotmp_name
será"none"
.error
: é o código de erro que descreve o status do upload, por exemplo,UPLOAD_ERR_OK
significa que o arquivo foi carregado com sucesso. Mais mensagens de erro aqui .
O seguinte define MESSAGES
uma constante que mapeia o código de erro com a mensagem correspondente:
const MESSAGES = [
UPLOAD_ERR_OK => 'File uploaded successfully',
UPLOAD_ERR_INI_SIZE => 'File is too big to upload',
UPLOAD_ERR_FORM_SIZE => 'File is too big to upload',
UPLOAD_ERR_PARTIAL => 'File was only partially uploaded',
UPLOAD_ERR_NO_FILE => 'No file was uploaded',
UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder on the server',
UPLOAD_ERR_CANT_WRITE => 'File is failed to save to disk.',
UPLOAD_ERR_EXTENSION => 'File is not allowed to upload to this server',
];
Linguagem de código: PHP ( php )
Se você deseja receber uma mensagem baseada em um código de erro, basta procurá-lo no MESSAGES
array assim:
$message = MESSAGES[$_FILES['file']['error']];
Linguagem de código: PHP ( php )
Quando um arquivo é carregado com sucesso, ele é armazenado em um diretório temporário no servidor. E você pode usar a move_uploaded_file()
função para mover o arquivo do diretório temporário para outro.
A move_uploaded_file()
função aceita dois argumentos:
filename
: é o nome do arquivo enviado, que é$_FILES['file']['tmp_name']
.destination
: é o destino do arquivo movido.
A move_uploaded_file()
função retorna true
se mover o arquivo com sucesso; caso contrário, ele retorna false
.
Medidas de segurança
Todas as informações na $_FILES
variável não são confiáveis, exceto o arquivo tmp_name
. Os hackers podem manipular $_FILES
e enviar o script malicioso para o servidor.
Para evitar isso, você precisa validar as informações no arquivo $_FILES
.
Primeiro, verifique se o nome de entrada do arquivo está na $_FILES
variável usando isset()
:
if(! isset($_FILES['file']) ) {
// error
}
Linguagem de código: PHP ( php )
Neste exemplo, the 'file'
é o nome do elemento de entrada do arquivo.
Segundo, verifique o tamanho real do arquivo chamando a filesize()
função e compare seu resultado com o tamanho máximo permitido do arquivo. Não deve confiar no size
fornecido pelo $_FILES
. Por exemplo:
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
if (filesize($_FILES['file']['tmp_name']) > MAX_SIZE) {
// error
}
Linguagem de código: PHP ( php )
Observe que o MAX_SIZE
não deve ser maior que upload_max_filesize
o especificado no arquivo php.ini
.
O tamanho de um arquivo está em bytes, o que não é legível por humanos. Para torná-lo mais legível, podemos definir uma função que converte os bytes para um formato legível por humanos, por exemplo, 1,20M, 2,51G:
function format_filesize(int $bytes, int $decimals = 2): string
{
$units = 'BKMGTP';
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . $units[(int)$factor];
}
Linguagem de código: PHP ( php )
Terceiro, valide o tipo MIME do arquivo em relação aos tipos de arquivo permitidos. Para fazer isso, você precisa definir uma lista de arquivos permitidos:
const ALLOWED_FILES = [
'image/png' => 'png',
'image/jpeg' => 'jpg'
];
Linguagem de código: PHP ( php )
Para obter o tipo MIME real de um arquivo, você usa três funções: finfo_open()
, finfo_file()
e finfo_close()
.
- O
finfo_open()
retorna um novofileinfo
recurso. - O
finfo_file()
retorna as informações sobre o arquivo. - O
finfo_close()
fecha ofileinfo
recurso.
Para torná-lo fácil e reutilizável, você pode definir uma função get_mime_type()
como esta:
function get_mime_type(string $filename)
{
$info = finfo_open(FILEINFO_MIME_TYPE);
if (!$info) {
return false;
}
$mime_type = finfo_file($info, $filename);
finfo_close($info);
return $mime_type;
}
Linguagem de código: PHP ( php )
A get_mime_type()
função aceita um nome de arquivo e retorna o tipo MIME do arquivo. Ele retornará false
se ocorrer um erro.
Observe que a Internet Assigned Numbers Authority (IANA) é responsável por todos os tipos MIME oficiais e você pode encontrar a lista completa na página de tipos MIME .
Se ocorrer um erro ou a validação falhar, você pode definir uma mensagem flash e redirecionar o navegador de volta para a página de upload. A função a seguir define uma mensagem flash e executa um redirecionamento:
function redirect_with_message(string $message, string $type=FLASH_ERROR, string $name='upload', string $location='index.php'): void
{
flash($name, $message, $type);
header("Location: $location", true, 303);
exit;
}
Linguagem de código: PHP ( php )
Observe que usamos a flash()
função definida no flash.php
arquivo. A flash()
função mostra uma mensagem flash baseada em sessão. Confira o tutorial da mensagem flash aqui .
O seguinte mostra como usar a redirection_with_message()
função:
if(error) {
redirect_with_message('An error occurred');
}
Linguagem de código: JavaScript ( javascript )
A return
instrução encerra o script atual.
Como todas essas funções get_mime_type()
, format_filesize()
, e redirect_with_message()
são reutilizáveis, você pode adicioná-las ao functions.php
arquivo assim:
<?php
/**
* Messages associated with the upload error code
*/
const MESSAGES = [
UPLOAD_ERR_OK => 'File uploaded successfully',
UPLOAD_ERR_INI_SIZE => 'File is too big to upload',
UPLOAD_ERR_FORM_SIZE => 'File is too big to upload',
UPLOAD_ERR_PARTIAL => 'File was only partially uploaded',
UPLOAD_ERR_NO_FILE => 'No file was uploaded',
UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder on the server',
UPLOAD_ERR_CANT_WRITE => 'File is failed to save to disk.',
UPLOAD_ERR_EXTENSION => 'File is not allowed to upload to this server',
];
/**
* Return a mime type of file or false if an error occurred
*
* @param string $filename
* @return string | bool
*/
function get_mime_type(string $filename)
{
$info = finfo_open(FILEINFO_MIME_TYPE);
if (!$info) {
return false;
}
$mime_type = finfo_file($info, $filename);
finfo_close($info);
return $mime_type;
}
/**
* Return a human-readable file size
*
* @param int $bytes
* @param int $decimals
* @return string
*/
function format_filesize(int $bytes, int $decimals = 2): string
{
$units = 'BKMGTP';
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . $units[(int)$factor];
}
/**
* Redirect user with a session based flash message
* @param string $message
* @param string $type
* @param string $name
* @param string $location
* @return void
*/
function redirect_with_message(string $message, string $type=FLASH_ERROR, string $name='upload', string $location='index.php'): void
{
flash($name, $message, $type);
header("Location: $location", true, 303);
exit;
}
Linguagem de código: HTML, XML ( xml )
Usando o campo de formulário MAX_FILE_SIZE
Se você colocar um campo com o nome MAX_FILE_SIZE
antes de um elemento de entrada de arquivo no formulário, o PHP usará esse valor em vez de upload_max_filesize
validar o tamanho do arquivo.
Por exemplo:
<form enctype="multipart/form-data" action="upload.php" method="post">
<div>
<label for="file">Select a file:</label>
<input type="hidden" name="MAX_FILE_SIZE" value="10240"/>
<input type="file" id="file" name="file"/>
</div>
<div>
<button type="submit">Upload</button>
</div>
</form>
Linguagem de código: HTML, XML ( xml )
Neste exemplo, MAX_FILE_SIZE
é 10KB
. Se você fizer upload de um arquivo maior que 10KB
o PHP, ocorrerá um erro. No entanto, é fácil manipular esse campo, portanto você nunca deve confiar nele para fins de segurança.
Observe que você não pode definir um MAX_FILE_SIZE
valor maior que a upload_max_filesize
diretiva no php.ini
arquivo.
Exemplo de upload de arquivo PHP
Primeiro, crie a seguinte estrutura de diretórios:
├── inc
| ├── flash.php
| └── functions.php
├── index.php
├── upload.php
└── uploads
Linguagem de código: texto simples ( texto simples )
Em segundo lugar, adicione o seguinte formulário de upload de arquivo ao index.php
arquivo:
<?php
session_start();
require_once __DIR__ . '/inc/flash.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="https://tutorials.acervolima.com/app/css/style.css"/>
<title>PHP File Upload</title>
</head>
<body>
<?php flash('upload') ?>
<main>
<form enctype="multipart/form-data" action="upload.php" method="post">
<div>
<label for="file">Select a file:</label>
<input type="file" id="file" name="file"/>
</div>
<div>
<button type="submit">Upload</button>
</div>
</form>
</main>
</body>
</html>
Linguagem de código: PHP ( php )
O index.php
arquivo também contém um formulário para upload de um arquivo. O upload.php
arquivo cuidará do upload.
Terceiro, adicione o seguinte código ao upload.php
arquivo para processar o arquivo carregado:
<?php
session_start();
require_once __DIR__ . '/inc/flash.php';
require_once __DIR__ . '/inc/functions.php';
const ALLOWED_FILES = [
'image/png' => 'png',
'image/jpeg' => 'jpg'
];
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
const UPLOAD_DIR = __DIR__ . '/uploads';
$is_post_request = strtolower($_SERVER['REQUEST_METHOD']) === 'post';
$has_file = isset($_FILES['file']);
if (!$is_post_request || !$has_file) {
redirect_with_message('Invalid file upload operation', FLASH_ERROR);
}
//
$status = $_FILES['file']['error'];
$filename = $_FILES['file']['name'];
$tmp = $_FILES['file']['tmp_name'];
// an error occurs
if ($status !== UPLOAD_ERR_OK) {
redirect_with_message($messages[$status], FLASH_ERROR);
}
// validate the file size
$filesize = filesize($tmp);
if ($filesize > MAX_SIZE) {
redirect_with_message('Error! your file size is ' . format_filesize($filesize) . ' , which is bigger than allowed size ' . format_filesize(MAX_SIZE), FLASH_ERROR);
}
// validate the file type
$mime_type = get_mime_type($tmp);
if (!in_array($mime_type, array_keys(ALLOWED_FILES))) {
redirect_with_message('The file type is not allowed to upload', FLASH_ERROR);
}
// set the filename as the basename + extension
$uploaded_file = pathinfo($filename, PATHINFO_FILENAME) . '.' . ALLOWED_FILES[$mime_type];
// new file location
$filepath = UPLOAD_DIR . '/' . $uploaded_file;
// move the file to the upload dir
$success = move_uploaded_file($tmp, $filepath);
if ($success) {
redirect_with_message('The file was uploaded successfully.', FLASH_SUCCESS);
}
redirect_with_message('Error moving the file to the upload folder.', FLASH_ERROR);
Linguagem de código: PHP ( php )
Como upload.php
funciona.
1) Inicie a sessão e inclua os arquivos flash.php
e functions.php
para que possamos utilizar as funções utilitárias definidas acima:
session_start();
require_once __DIR__ . '/inc/flash.php';
require_once __DIR__ . '/inc/functions.php';
Linguagem de código: PHP ( php )
2) Defina um array que especifique os arquivos permitidos:
const ALLOWED_FILES = [
'image/png' => 'png',
'image/jpeg' => 'jpg'
];
Linguagem de código: PHP ( php )
3) Defina uma constante que especifique o tamanho máximo do arquivo:
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
Linguagem de código: JavaScript ( javascript )
3) Defina o diretório de upload que armazena os arquivos enviados:
const UPLOAD_DIR = __DIR__ . '/uploads';
Linguagem de código: PHP ( php )
4) Retorne uma mensagem de erro se o método de solicitação não estiver POST
ou file
não existir na $_FILES
variável:
$is_post_request = strtolower($_SERVER['REQUEST_METHOD']) === 'post';
$has_file = isset($_FILES['file']);
if (!$is_post_request || !$has_file) {
redirect_with_message('Invalid file upload operation', FLASH_ERROR);
}
Linguagem de código: PHP ( php )
5) Obtenha as informações do arquivo carregado, incluindo erro, nome do arquivo e nome do arquivo temporário:
$status = $_FILES['file']['error'];
$filename = $_FILES['file']['name'];
$tmp = $_FILES['file']['tmp_name'];
Linguagem de código: PHP ( php )
6) Retorne uma mensagem de erro se o upload do arquivo falhou:
if ($status !== UPLOAD_ERR_OK) {
redirect_with_message(MESSAGES[$status], FLASH_ERROR);
}
Linguagem de código: PHP ( php )
7) Obtenha o tamanho do arquivo na pasta temporária e compare-o com MAX_SIZE. Se o tamanho do arquivo enviado for maior que MAX_SIZE, emita um erro:
// validate the file size
$filesize = filesize($tmp);
if ($filesize > MAX_SIZE) {
redirect_with_message('Error! your file size is ' . format_filesize($filesize) . ' , which is bigger than allowed size ' . format_filesize(MAX_SIZE), FLASH_ERROR);
}
Linguagem de código: PHP ( php )
8) Obtenha o tipo MIME e compare-o com o tipo MIME dos arquivos permitidos especificados no array ALLOWED_FILES; emitir um erro se a validação falhar:
$mime_type = get_mime_type($tmp);
if (!in_array($mime_type, array_keys(ALLOWED_FILES))) {
redirect_with_message('The file type is not allowed to upload', FLASH_ERROR);
}
Linguagem de código: PHP ( php )
9) Construa um novo nome de arquivo concatenando o nome do arquivo carregado com a extensão de arquivo válida.
// set the filename as the basename + extension
$uploaded_file = pathinfo($filename, PATHINFO_FILENAME) . '.' . ALLOWED_FILES[$mime_type];
Linguagem de código: PHP ( php )
Observe que o seguinte pathinfo()
retorna o nome do arquivo sem a extensão:
pathinfo($filename, PATHINFO_FILENAME)
Linguagem de código: PHP ( php )
Por exemplo, se $filename
for example.jpg
, retornará o example
único.
10) Mova o arquivo do diretório temporário para a pasta de upload e emita uma mensagem de erro ou sucesso dependendo do resultado da move_uploaded_file()
função:
$filepath = UPLOAD_DIR . '/' . $uploaded_file;
// move the file to the upload dir
$success = move_uploaded_file($tmp, $filepath);
if ($success) {
redirect_with_message('The file was uploaded successfully.', FLASH_SUCCESS);
}
redirect_with_message('Error moving the file to the upload directory.', FLASH_ERROR);
Linguagem de código: PHP ( php )
Resumo
- Use
input
withtype="file"
para criar um elemento de entrada de arquivo e inclua oenctype="multipart/form-data"
atributo no formulário para permitir o upload do arquivo. - Acesse as informações do arquivo carregado por meio do
$_FILES
array. - Nunca confie nas informações do
$_FILES
exceto otmp_name
. - Sempre valide as informações no
$_FILES
. - Use a
move_uploaded_file()
função para mover o arquivo do diretório temporário para outra pasta.