Upload dinâmico com PHP/jQuery

Olá pessoal, tudo certo? Várias pessoas me perguntam como fazer upload de arquivos com ajax e hoje pretendo apresentar uma solução para isso. Na verdade, não se trata de um upload com ajax, mas sim um upload dinâmico, porém para um usuário comum isso é indiferente.

A solução é simples e conhecida: fazer um iframe contendo um campo do tipo file, fazemos o envio desse arquivo dentro do iframe e então com a ajuda do jQuery recuperamos as informações desse arquivo e trazemos para a página pai. Nesse artigo irei utilizar anexo de arquivos como exemplo.

Confira o resultado final

1. Upload (upload.php)

Inicialmente iremos fazer uma página que fará o upload de um arquivo, assim como é feito normalmente. Caso você tenha dúvidas, pode consultar esses dois artigos: Upload simples de imagem com PHP/MySQL e Upload de vários arquivos com PHP.

<?php
// Flag que indica se há erro ou não
$erro = null;
// Quando enviado o formulário
if (isset($_FILES['arquivo']))
{
    // Extensões permitidas
    $extensoes = array(".doc", ".txt", ".pdf", ".docx", ".jpg");
    // Caminho onde ficarão os arquivos
    $caminho = "uploads/";
    // Recuperando informações do arquivo
    $nome = $_FILES['arquivo']['name'];
    $temp = $_FILES['arquivo']['tmp_name'];
    // Verifica se a extensão é permitida
    if (!in_array(strtolower(strrchr($nome, ".")), $extensoes)) {
		$erro = 'Extensão inválida';
	}
    // Se não houver erro
    if (!isset($erro)) {
        // Gerando um nome aleatório para o arquivo
        $nomeAleatorio = md5(uniqid(time())) . strrchr($nome, ".");
        // Movendo arquivo para servidor
        if (!move_uploaded_file($temp, $caminho . $nomeAleatorio))
            $erro = 'Não foi possível anexar o arquivo';
    }
}
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Upload dinâmico com jQuery/PHP</title>
    
    <script type="text/javascript" src="js/jquery.js"></script>
</head>

<body>

<form id="upload" action="upload.php" method="post" enctype="multipart/form-data">
    <label>Arquivo: </label> <span id="status" style="display: none;"><img src="image/loader.gif" alt="Enviando..." /></span> <br />
    <input type="file" name="arquivo" id="arquivo" />
</form>

</body>
</html>

Bom pessoal, nesse arquivo será feito apenas um upload simples. Resumindo, quando enviado o formulário nós recuperamos as informações desse arquivo, verificamos se sua extensão é válida, caso seja, geramos um nome aleatório para esse arquivo e enviamos para o servidor.

Vamos agora criar nosso código javascript (utilizando jQuery), que será incluído no cabeçalho desse arquivo:

 $(function($) {
        // Definindo página pai
        var pai = window.parent.document;
        
        <?php if (isset($erro)): // Se houver algum erro ?>
        
            // Exibimos o erro
            alert('<?php echo $erro ?>');
            
        <?php elseif (isset($nome)): // Se não houver erro e o arquivo foi enviado ?>
        
            // Adicionamos um item na lista (ul) que tem ID igual a "anexos"
            $('ul#anexos', pai).append('<li lang="<?php echo $nomeAleatorio ?>"><?php echo $nome ?> <img src="image/remove.png" alt="Remover" class="remover" onclick="removeAnexo(this)" \/> </li>');
            
        <?php endif ?>
        
        // Quando selecionado um arquivo
    	$("#arquivo").change(function() {	    
            // Se o arquivo foi selecionado
            if (this.value != "")
            {    
                // Exibimos o loder
                $("#status").show();
                // Enviamos o formulário
                $("#upload").submit();
            }
        });
    });

Talvez não faça tanto sentido agora, porém mais adiante você entenderá melhor, vamos aos detalhes:

  • Linha 3: Como esse arquivo ficará dentro de um iframe, precisamos saber quem é a página pai, para isso definimos uma variável com o documento da página pai;
  • Linha 5, 8: Aqui está um detalhe interessante. Como a linguagem servidor (PHP) é executada primeiro que a cliente (javascript), nós fazemos um teste. Se a variável erro foi definida, nós imprimimos um alert, que depois será executada pelo javascript, que exibirá uma mensagem de alerta com o erro que foi armazenado;
  • Linha 10: Caso não haja erro, e a variável nome foi definida, ou seja, o arquivo foi enviado;
  • Linha 13: Colocamos na lista (que será criada adiante) com ID igual a anexos um item, porém perceba que foi passado no segundo parâmetro do seletor a variável pai, portanto ele irá buscar o ul#anexos da página pai. Nesse item colocamos o nome aleatório do arquivo na propriedade LANG. Logo após, colocamos uma imagem que será responsável por remover o anexo, note que definimos, no evento onclick, a chamada da função removeAnexo(), está será explicada adiante.
  • Linha 18: Quando o usuário selecionar um arquivo.
  • Linha 20: Verificamos se um arquivo foi realmente selecionado.
  • Linha 23: Exibimos nosso loader (carregando).
  • Linha 25: Enviamos o formulário via javascript.

2. A página pai (index.php)

Está será a página principal, onde colocaremos o iframe que incluirá o nosso arquivo de upload:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Upload dinâmico com jQuery/PHP</title>

<script type="text/javascript" src="js/jquery.js"></script>

</head>

<body>

<h1>Upload dinâmico com jQuery/PHP</h1>

<ul id="anexos"></ul>
<iframe src="upload.php" frameborder="0" scrolling="no"></iframe>
    
<form id="upload" action="index.php" method="post">
    <input type="submit" name="enviar" value="Enviar" />
</form>

</body>
</html>

Nessa estrutura, criamos a lista (ul#anexos) onde serão adicionados os anexos, logo abaixo criamos nosso iframe e chamamos nosso arquivo de upload (upload.php). A essa altura, nosso upload dinâmico já está “funcional”, porém precisamos criar a função para remover o anexo:

// Função para remover um anexo
function removeAnexo(obj)
{
    // Recuperando nome do arquivo
    var arquivo = $(obj).parent('li').attr('lang');
    // Removendo arquivo do servidor
    $.post("index.php", {acao: 'removeAnexo', arquivo: arquivo}, function() {
        // Removendo elemento da página
        $(obj).parent('li').remove();
    });
}
  • Linha 2: Criamos nossa função que receberá como parâmetro o elemento da imagem;
  • Linha 5: A imagem está dentro do item (li), certo? Portanto, através do método parent() selecionamos o item (li) e pegamos o valor da propriedade LANG (Lembra que colocamos o nome do arquivo aqui?);
  • Linha 7: Fazemos uma requisição ajax com nosso próprio arquivo, e passamos como ação ‘removeAnexo’ e também o nome do arquivo anexado. Isso será preciso para que possamos remover o arquivo do servidor;
  • Linha 9: Quando terminado a requisição, removemos o elemento li de nossa página.

Vamos então criar a função PHP (logo no inicio do arquivo) que removerá o arquivo do servidor:

<?php
// Quando a ação for para remover anexo
if ($_POST['acao'] == 'removeAnexo')
{
    // Recuperando nome do arquivo
    $arquivo = $_POST['arquivo'];
    // Caminho dos uploads
    $caminho = 'uploads/';
    // Verificando se o arquivo realmente existe
    if (file_exists($caminho . $arquivo) and !empty($arquivo))
        // Removendo arquivo
        unlink($caminho . $arquivo);
    // Finaliza a requisição
    exit;
}
?>

Parece que agora acabou né? Ainda não. Como iremos recuperar esses anexos quando enviado o formulário? Já que nosso formulário está simplesmente assim:

<form id="upload" action="index.php" method="post">
    <input type="submit" name="enviar" value="Enviar" />
</form>

A solução é criar campos ocultos com os nomes dos arquivos quando enviado o formulário, para isso:

$(function($) {
    // Quando enviado o formulário
    $("#upload").submit(function() {
        // Passando por cada anexo
        $("#anexos").find("li").each(function() {
            // Recuperando nome do arquivo
            var arquivo = $(this).attr('lang');
            // Criando campo oculto com o nome do arquivo
            $("#upload").prepend('<input type="hidden" name="anexos[]" value="' + arquivo + '" \/>');
        }); 
    });
});
  • Linha 3: Quando nosso formulário for enviado;
  • Linha 5: Procuramos cada item (li) em nossa lista de anexo e passamos por cada um através do laço each();
  • Linha 7: Recuperamos o nome (aleatório) do arquivo que foi armazenado na propriedade LANG;
  • Linha 9: Através do prepend() adicionamos um campo oculto (hidden) dentro de nosso formulário (#upload) e colocamos o nome do arquivo como valor. Note que atribuímos o nome como anexos[] para que assim possamos recuperar os anexos já dentro de um array com PHP.

Agora, para fins de teste você pode adicionar o seguinte no inicio do arquivo:

// Se enviado o formulário
if (isset($_POST['enviar']))
{
    echo 'Arquivos enviados: ';
    echo '<pre>';
        // Exibimos os arquivos anexados
        print_r($_POST['anexos']);
    echo '</pre>';
}

Assim, você pode perceber, que quando enviado o formulário, o nome de nossos arquivos ficaram no array anexos e então podemos armazená-los no banco de dados, por exemplo.

3. Finalizando

Bom pessoal, meu objetivo nesse artigo foi apresentar uma alternativa para “simular” um upload com ajax. Se você tiver outra alternativa, compartilhe conosco. Dúvidas ou críticas construtivas são sempre bem vindas.

Download dos arquivos

Espero que vocês tenham entendido, até a próxima.
Abraços.