Ir para o conteúdo

Upload de ficheiros

Vamos então começar pelo HTML...

Ora, nós já sabemos que existem 2 métodos (principais, ou mais utilizados embora existam outros menos usados) de envio de informação por parte de forms em HTML: o POST e o GET.

Ora o GET usa o endereço (URL) para enviar os dados. O POST usa o corpo do protocolo HTTP para enviar os dados. Dá-se preferência ao segundo método visto que o URL tem um tamanho limitado (salvo erro) a 4 KB, enquanto que o POST pode ser usado à vontade, quer para grandes textos, quer para ficheiros.

Além disso, o método POST é mais discreto, pois passa os dados em segundo-plano sem que o utilizador (comum) dê por isso e acima de tudo, enquanto os URLs são muitas vezes guardados por servidores proxy e de ISPs, os POSTs não o são (pelo menos não é tão comum vê-los registar os POSTs).

Sabendo isto, aconselho vivamente o método POST para forms para envio de ficheiros. Será também o tipo de método que vou utilizar nos exemplos.

Além do atributo method, existe outro atributo extremamente essencial para que o ficheiro seja enviado correctamente. E esse atributo é o enctype.

O enctype determina o tipo de codificação que os dados do form devem levar antes de serem enviados para o servidor (mais info ver: http://www.w3schools.com/tags/att_form_enctype.asp). Para o envio de ficheiros ser feito correctamente o atributo enctype deve estar definido como multipart/form-data.

Claro, depois temos os objectos que devemos usar no site... logicamente que para o envio de ficheiros precisamos de um input do tipo file, como poderão ver já de seguida:

<!DOCTYPE html>
<html>
<head>
    <title>Teste de Envio/Upload de Ficheiros</title>
</head>
<body>
    <form name="frmUpload" method="post" enctype="multipart/form-data" action="upload.php">
        <label for="fileObj">Seleccione um ficheiro</label>
        <input type="file" name="fileUpload" id="fileObj">
        <button type="submit" name="cmdSend">Enviar</button>
    </form>
</body>
</html>

Ora pronto, aqui temos um form simples de envio de ficheiros... Este form poderia ainda ter outros campos normais de texto, ou checkboxes, ou qualquer outro tipo de objecto dos forms. Isto foi um exemplo meramente indicativo.

Como podem analisar, ao pressionar o botão Enviar, ele irá enviar os dados para um ficheiro chamado upload.php para processar o envio do ficheiro. Podíamos colocar o processo do upload todo no mesmo script (ou seja o form e o processamento do envio) mas para simplificar, vamos separar as águas.

Já temos o HTML... Bora para o PHP?

Até agora pouco ou nada de novo têm aprendido. E durante esta parte inicial vão ficar praticamente iguais, pois vou passar a explicar os conceitos necessários em PHP para percebermos o código.

Primeiro, sabendo que estamos a usar o enctype="multipart/form-data, ao fazer o POST do form, o protocolo HTTP (que é o protocolo de comunicação usado para processar as páginas e enviar e receber pedidos de HTML e o próprio HTML) vai fazer o upload do ficheiro para uma pasta temporária do servidor, normalmente em contas de alojamento existe uma pasta chamada tmp na raiz da vossa conta... é para lá que eles vão (normalmente).

Segundo, mais uma vez, sabendo que estamos a usar o enctype="multipart/form-data", ao fazer o POST do form para o upload.php, o PHP vai fazer o seguinte:

  1. Pegar nos objectos que não são do tipo file e defini-los na variável superglobal $_POST (para quem não sabe o que são variáveis superglobais, ver http://php.net/manual/en/language.variables.superglobals.php).
  2. Pegar nos objectos que são do tipo file e defini-los na variável superglobal $_FILES (para quem não conhece esta variável superglobal, ver http://www.php.net/manual/pt_BR/features.file-upload.post-method.php).

De referir que o servidor apaga os ficheiros da pasta temporária após o final do processamento do script para onde é enviado o POST.

Sabendo isto, vamos agora começar a estipular algumas regras, tais como:

  1. Os ficheiros enviados para o servidor, são para guardar numa pasta específica? Se sim qual? Se não, então irá ser para lê-lo e efectuar uma operação específica (que nao é o nosso caso).
  2. Que tipos de ficheiro vamos aceitar? Só imagens... Ficheiros de texto, ficheiros PDF, documentos do Word, executáveis, zips... Todos excepto alguns específicos... Vocês é que sabem.
  3. Os dados do ficheiro são para guardar na base de dados, guardar noutro local ou simplesmente ignorar?
  4. Os ficheiros terão os nomes originais ou ficarão com nomes dados por mim?
  5. Os ficheiros terão alguma restrição além da extensão?

Ora as minhas definições face a estas perguntas, para o nosso exemplo, são:

  1. Os ficheiros serão enviados para uma pasta específica, neste caso uma pasta que está no mesmo directório que o script upload.php e que terá o nome de ficheiros;
  2. De modo a coincidir com alguns pedidos, vou colocar as seguintes extensões: .doc, .zip, .pdf, .txt, .jpg, .png, .odt.
  3. Eu, preferencialmente, uso a base de dados, porque normalmente até preciso de mostrar só um e não vou a andar a procurar na pasta um específico, prefiro ir directo pelos dados guardados na base de dados. Portanto, sim, vamos usar a base de dados.
  4. Podem usar os nomes originais, mas eu não recomendo, pois podem resultar problemas daí tais como substituição de ficheiros e/ou erros de upload porque o ficheiro está a ser usado, etc. Eu prefiro dar-lhes um nome único gerado dinamicamente e registado na base de dados.
  5. Vou restringir o tamanho a um máximo de 2MB. Porque o php.ini pode estar com um valor superior e um documento de texto maior que 2MB para mim, não me faz jeito (podem alterar depois).

Feito isto, resta-me dar-vos uns links para o php.net de algumas funções que vou usar no exemplo e depois... ver a prática.

Exemplo Prático

  /**
   * Só para não esquecerem:
   * SCRIPT:                                     upload.php
   * METHOD:                                   post
   * ENCTYPE:                                  multipart/form-data
   * SUPERGLOBALS:                        $_FILES,$_POST
   * EXTENSOES PERMITIDAS:          .doc,.zip,.pdf,.txt,.jpg,.png,.odt
   * PASTA FINAL:                            ./ficheiros
   * TAMANHO MÁXIMO:                   2MB
   * TAMANHO MÍNIMO:                    >0 MB
   **/

define("MAX_SIZE",(2*1024*1024)); // VALOR EM bytes DE 2MB
define("UPLOAD_PATH","./ficheiros/");

 // PRIMEIRO CONFIRMAMOS SE FOI POSTADO ALGUMA COISA E SE FOI CARREGADO O BOTÃO ENVIAR
if( isset($_POST) )
{
     // SEGUNDO VERIFICAMOS SE FOI ENVIADO ALGUM FICHEIRO E SE FOI ATRAVÉS DO NOSSO OBJECTO 'fileUpload' E SE O NOME DO FICHEIRO NÃO É "" (ou seja nenhum ;) )
    if( isset($_FILES) && isset($_FILES['fileUpload']) && (trim($_FILES['fileUpload']['name'])!=""))
    {
        // TERCEIRO, INICIALIZO UM ARRAY QUE VAI CONTAR AS EXTENSÕES PERMITIDAS...
        $extensoesPermitidas=array("doc","zip","pdf","txt","jpg","png","odt");

        // CRIO UMA VARIÁVEL PARA TRABALHAR DIRECTAMENTE COM OS DADOS DO UPLOAD DO FICHEIRO
        $ficheiro=$_FILES['fileUpload'];

         // ... DEPOIS OBTENHO AS INFORMAÇÕES DO FICHEIRO ENVIADO...
        $file_info=pathinfo($ficheiro['name']); // ISTO VAI-ME RETORNAR UM ARRAY COM OS DADOS DO FICHEIRO

         // ... E AGORA VALIDO SE A EXTENSÃO É UMA EXTENSÃO VÁLIDA...
        if(in_array($file_info['extension'],$extensoesPermitidas))
        {
             // A EXTENSÃO É VÁLIDA... E O TAMANHO?
            $file_size=filesize($ficheiro['tmp_name']);
            if(($file_size<=MAX_SIZE) && ($file_size>0))
            {
                // QUARTO :: O TAMANHO É O IDEAL... VAMOS FAZER O UPLOAD?
                $filename=uniqid(time()) . "." . $file_info['extension']; // NOME DO FICHEIRO GERADO
                if(move_uploaded_file($ficheiro['tmp_name'],UPLOAD_PATH . $filename))
                {
                    // O UPLOAD FOI FEITO COM SUCESSO, AGORA JÁ PODEMOS GUARDAR NA BASE DE DADOS
                    if(!mysql_connect('localhost','user','password') || !mysql_select_db('basededados'))
                    {
                        // UPS... NAO CONSEGUI LIGAR-ME AO SERVIDOR DA BD OU À BASE DE DADOS... 
                        // ENTAO O FICHEIRO NAO PODE FICAR CÁ....
                        unlink(UPLOAD_PATH . $filename);

                        // E DAMOS O ERRO AO USER
                        die("O UPLOAD FALHOU :: CONTACTE O SUPORTE TÉCNICO :: ERRO 1");
                    }
                    else
                    {
                        // ESTÁ TUDO BEM, VAMOS ENTÃO GUARDAR OS DADOS...
                        // VOU GUARDAR NUMA TABELA CHAMADA ficheiros COM OS SEGUINTES CAMPOS:
                        // ID,nome,tamanho,mime,dataregisto,width,height (os 2 ultimos campos sao preenchidos com valores>0 caso seja uma imagem)
                        $tamanhoImg=array(0,0);
                        $extensaoImagens=array("jpg","png");
                        if(in_array($file_info['extension'],$extensaoImagens))
                            $tamanhoImg=getimagesize(UPLOAD_PATH . $filename);

                        mysql_query("INSERT INTO ficheiros (nome,tamanho,mime,dataregisto,width,height) VALUES ('$filename',$file_size,'{$ficheiro['type']}',NOW(),{$tamanhoImg[0]},{$tamanhoImg[1]})");
                        die("UPLOAD FEITO COM SUCESSO");
                    }
                }
                else
                    die("O UPLOAD FALHOU :: CONTACTE O SUPORTE TÉCNICO :: ERRO 2"); // PROVAVELMENTE OU SÃO FALTA DE PERMISSÕES DE ESCRITA NA PASTA ./ficheiros/ OU ALGO SE PASSOU COM O FICHEIRO AO FAZER MOVE_UPLOAD... 

            }
            elseif($file_size>MAX_SIZE)
                die("O FICHEIRO É SUPERIOR A 2 MB!");
            else
                die("FICHEIRO INVÁLIDO OU TEM 0 MB");
        }
        else 
            die("EXTENSÃO INVÁLIDA. APENAS SÃO SUPORTADAS AS SEGUINTES EXTENSÕES: ." . implode(",.",$extensoesPermitidas));

    }
    else
        die("SELECCIONE UM FICHEIRO");

}
else
    die("USE O NOSSO FORMULÁRIO DE ENVIO DE FICHEIROS");