Upload de arquivo PHP

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 valuedo <input>elemento conterá o caminho para o arquivo selecionado. Para fazer upload de vários arquivos, você adiciona o multipleatributo ao <input>elemento assim:

<input type="file" id="file" name="file" multiple>Linguagem de código:  HTML, XML  ( xml )

Neste caso, o valueatributo 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 acceptatributo. O valor do acceptatributo é 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 .pngde .jpegimagens:

<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 enctypeatributo 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.iniarquivo. Se você não sabe onde encontrar seu php.iniarquivo, 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.iniLinguagem de código:  CSS  ( css )

Aqui estão as configurações importantes para uploads de arquivos no php.iniarquivo:

; 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=20Linguagem de código:  texto simples  ( texto simples )

uploads_de_arquivos

A file_uploaddiretiva deveria ser Onpermitir o upload de arquivos. O padrão é On.

upload_max_filesize

Especifica upload_max_filesizeo 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_dirdiretório que armazena os arquivos carregados temporariamente.

post_max_size

O post_max_sizeespecifica o tamanho máximo dos POSTdados. Como você fará upload de arquivos com a POSTsolicitação, verifique se post_max_sizeé maior que upload_max_size.

max_file_uploads

A max_file_uploadsdiretiva 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 $_FILESarray. 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/jpegpara imagem JPEG ou application/pdfarquivo 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 arquivo tmp_nameserá "none".
  • error: é o código de erro que descreve o status do upload, por exemplo, UPLOAD_ERR_OKsignifica que o arquivo foi carregado com sucesso. Mais mensagens de erro aqui .

O seguinte define MESSAGESuma 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 MESSAGESarray 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 truese mover o arquivo com sucesso; caso contrário, ele retorna false.

Medidas de segurança

Todas as informações na $_FILESvariável não são confiáveis, exceto o arquivo tmp_name. Os hackers podem manipular $_FILESe 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 $_FILESvariá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 sizefornecido 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_SIZEnão deve ser maior que upload_max_filesizeo 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 novo fileinforecurso.
  • O finfo_file()retorna as informações sobre o arquivo.
  • O finfo_close()fecha o fileinforecurso.

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á falsese 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.phparquivo. 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 returninstruçã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.phparquivo 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_SIZEantes de um elemento de entrada de arquivo no formulário, o PHP usará esse valor em vez de upload_max_filesizevalidar 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 10KBo 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_SIZEvalor maior que a upload_max_filesizediretiva no php.iniarquivo.

Exemplo de upload de arquivo PHP

Primeiro, crie a seguinte estrutura de diretórios:

├── inc
|  ├── flash.php
|  └── functions.php
├── index.php
├── upload.php
└── uploadsLinguagem de código:  texto simples  ( texto simples )

Em segundo lugar, adicione o seguinte formulário de upload de arquivo ao index.phparquivo:

<?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.phparquivo também contém um formulário para upload de um arquivo. O upload.phparquivo cuidará do upload.

Terceiro, adicione o seguinte código ao upload.phparquivo 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.phpfunciona.

1) Inicie a sessão e inclua os arquivos flash.phpe functions.phppara 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; //  5MBLinguagem 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 POSTou filenão existir na $_FILESvariá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 $filenamefor 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 inputwith type="file"para criar um elemento de entrada de arquivo e inclua o enctype="multipart/form-data"atributo no formulário para permitir o upload do arquivo.
  • Acesse as informações do arquivo carregado por meio do $_FILESarray.
  • Nunca confie nas informações do $_FILESexceto o tmp_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.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *