Aprenda de uma vez a fazer upload de arquivos e imagens com PHP

O processo de uploads é simples, porém requer várias camadas de validação para um upload seguro.

Criando uma aplicação com área administrativa utilizando o Laravel

Realizar o upload de um arquivo é bem simples, podemos fazer isso, de várias formas, seja via FTP ou utilizando a classe move_uploaded_file, por exemplo.

O processo começa a ficar um pouco complexo quando aplicamos validações de tamanho, tipos de arquivos, existência de diretórios, permissões...e por ai vai, afina, qualquer pessoa pode subir qualquer tipo de porcaria se a gente não aplicar os filtros/validações.

É muito importante ter em mãos, algumas informações, então, veja a seguir algumas perguntas que deve ser feitas, já que é em cima de suas respostas que vamos construir o sistema.

  1. Que tipo de arquivo está permitido enviar?
  2. Qual o mimetypes deles?
  3. Qual tamanho máximo desses arquivos?
  4. Para onde vamos enviar?
  5. Qual o tamanho máximo permitido no arquivo de configuração?
  6. Quantidade máxima de arquivos é permitida?

No geral, é uma série de informações que precisamos levantar, e em nossa videoaula, tudo isso é esclarecido e apresentado de forma bem clara.

MIME Types

O MIME type é o mecanismo para dizer ao cliente a variedade de documentos transmitidos: a extensão de um nome de arquivo não tem significado na web. Portanto, é importante que o servidor esteja configurado corretamente, de modo que o MIME-type correto seja transmitido com cada documento. Os navegadores costumam usar o MIME-type para determinar qual ação usar como padrão para fazer quando um recurso é obtido. Fonte: Documentação Mozilla


Código fonte

A ordem de execução dos arquivos são:

1.O usuário cai direto na página de upload, chamada de index.html, na qual tem seu formulário e após envia-lo, os dados do método POST é enviado para o arquivo subir.php.
2.O arquivo subir.php gaz a instância da classe Upload, e recebe no método construtor a variável global $_FILES, contendo todas as informações de cada arquivo enviado.
3.Por último, chamados o método upload(), na qual podemos passar um parâmetro true para criar nomes aleatórios. Esse método é responsável por validar e enviar o arquivo para a pasta de destino, e ao término, retorna o código de resposta.


index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Uploads</title>
</head>

<body>

    <form action="subir.php" method="post" enctype="multipart/form-data">

        <input type="file" name="flArquivo">

        <button type="submit">Enviar</button>
    </form>

</body>

</html>

subir.php

<?php

//Requisitamos o arquivo com a classe de upload
require_once('upload.php');

//Instância da classe de upload e envio do $_FILES
$upload = new Upload($_FILES['flArquivo']);

//Chamamos o método que faz o envio e atribuimos true para renomear o nosso arquivo
$messageCode = $upload->upload(true);

//Exibimos o a mensagem a partir do código de resposta
echo $upload->getMessage($messageCode);

echo '<br/>';

echo $upload->getFilename();

//Função auxiliar para debugar
function dd($p = [])
{
    echo '<pre>';
    print_r($p);
    echo '</pre>';
}

upload.php

<?php

class Upload
{
    //$_FILE[]
    private $file;

    //Tipo mymes
    private $mimeTypes;

    //Tamanho máximo do arquivo em Byter
    private $maxSize;

    //Diretório de publicação
    private $path;

    //Nome do arquivo gerado
    private $fileName;

    /**
     * Método construtor
     *
     * @param  array $file $_FILES['yourFile']
     * @return void
     */
    public function __construct($file)
    {
        $this->file = $file;

        $this->mimeTypes = [
            'text/plain',
            'image/jpeg',
            'image/png'
        ];

        $this->maxSize = 2097152; //2MB

        $this->path = 'arquivos/';
    }

    /**
     * Método responsável por enviar o arquivo para o servidor
     *
     * @param  bool $rename
     * @return int
     */
    public function upload(bool $rename = false)
    {
        $validate = $this->validate();

        if ($validate < 0)
            return $validate;

        if ($rename) {
            $ex = explode('.', $this->file['name']);

            $this->fileName = uniqid('image_', true) . '.' . end($ex);
        } else {
            $this->fileName = preg_replace(
                '/[^a-z-A-Z0-1._-]/',
                '-',
                $this->file['name']
            );
        }

        if (!is_dir($this->path))
            mkdir($this->path, null, true);

        if (!move_uploaded_file(
            $this->file['tmp_name'],
            $this->path . $this->fileName
        ))
            return -3;

        return 1;
    }

    /**
     * Retorna o nome do arquivo
     *
     * @return string
     */
    public function getFilename()
    {
        return $this->fileName;
    }

    /**
     * Verifica se o arquivo que estamos subindo é válido
     *
     * @return int
     */
    public function validate()
    {
        $fileType = mime_content_type($this->file['tmp_name']);

        if (!in_array($fileType, $this->mimeTypes))
            return -1; //Tipo Inválido

        if ($this->file['size'] > $this->maxSize)
            return -2; //Arquivo muito grandee

        return 1;
    }

    /**
     * Retorna uma mensagem a partir do seu código
     *
     * @param  int $code
     * @return string
     */
    public function getMessage(int $code = null)
    {
        switch ($code) {
            case 1:
                return 'Arquivo enviado com sucesso.';
                break;

            case -1:
                return 'O tipo de arquivo é inválido.';
                break;

            case -2:
                return 'O tamanho do arquivo é maior do que o permitido.';
                break;

            case -3:
                return 'Houve um erro ao tentar subir o arquivo.';
                break;
        }
    }
}

Enfim, assim terminamos de enviar o nosso arquivo.