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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 $(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:

1
2
3
4
5
6
7
8
9
10
11
// 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:

1
2
3
4
5
6
7
8
9
10
11
12
$(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.

41 comments

  1. Como eu faço pra enviar varias de uma vez?

    Ou então enquanto o script esta enviando uma imagem o usuario pode estar selecionando outras imagens pra enviar?

  2. Iae blz Rafael,

    Estou tentando fazer o teu script selecionar varios arquivos segurando o shift como o gmail, facebook, orkut etc…
    No caso usei
    Você conseguiu fazer algo assim? sabe quais as modificações que tenho que fazer?

    Estou quebrando a cabeça e não consigo…

    Obrigado.

    Diego.

  3. Olá Rafael, primeiramente, parabéns pelo trabalho, você manda muito bem!

    Agora, gostaria de deixar uma dúvida: como seria caso eu tivesse esse envio dentro de outro form, e ele só enviasse as imagens no momento do envio do form, em vez de enviar automaticamente no momento do carregamento das imagens?

    Muito obrigado!

  4. Parabens pelo codigo!! Mandou mto bem, mas estou com problema no codigo.. o codigo eh o mesmo.. eu fiz o download e coloquei no meu apache.. ele funciona direitinho mas fica um erro aparecendo.. o erro eh esse

    Notice: Undefined index: acao in C:\Apache\htdocs\upload\Nova pasta\index.php on line 3

    se vc puder me ajudar eu agradeço
    Obrigado!

  5. Bom dia Rafael, Parebens pelo exemplo de Upload dinâmico com PHP/jQuery. Muito bom mesmo, vc é fera no assunto. A questao é o seguinte: para exibir as imagens enviadas a pasta pelo upload eu usei esse código.
    <?php

    $diretorio = "uploads";
    $arrayArquivos = scandir($diretorio);

    for($i=0; $i < count($arrayArquivos); $i++) {
    $arq = $arrayArquivos[$i];
    if($arq “.” && $arq “..”) {
    $ext = explode(“.”,$arq);
    if( ($ext[1] == “gif”) || ($ext[1] == “jpg”) || ($ext[1] == “png”) )
    {
    echo “ ”;
    }
    }

    }

    ?>

    Agora para excluir uma img da pasta, qual é o código que devo usar?

    Ficarei muito agradecido se puder me ajudar nessa.

    Forte abraço Silvio

  6. Parabéns pelos seus Posts… seus códigos são muito claros…

    Uma duvida, é possível limitar a quantidade de arquivos a serem anexados?

    Obrigado!

  7. Boa tarde!

    Rafael,

    em primeiro lugar gostaria de parabenizá-lo pelo post, me auxiliou muito com dúvida e também desejar um feliz 2012, meu velho estou com uma dúvida eu gostaria de poder selecionar vário arquivos por exemplo seu eu tive que enviar 900 fotos para meu website fica um pouco complicado de selecionar uma foto por vez, e tambem necessito escolher o diretório de destino das fotos. Pois cada cliente terá uma pasta exclusiva, consegues me ajudar…

    abraço

  8. Muito bom. Mas eu me embananei todo durante a explicação, ja tava ficando vezgo e com os miolos torrados.

    No meu site eu ofereço a possibilidade do visitante comprar um skin personalizado, bastando para isso enviar a imagem que ele quer que seja impressa. Essa imagem fica salva numa pasta de uma conta que tenho no dropbox. O problema é que a pessoa que compra o adesivo personalizado não renomeia a imagem e coloca nela o próprio nome antes de enviar pra mim. Fica difícil eu saber que imagem ela enviou, se para essa pasta sao enviadas diversas imagens todos os dias com nomes tipo SDC0098.jpg, imagemskin.jpg, borboletas.jpg, etc… por outros clientes que fazem a mesma coisa.
    Eu gostaria de criar um campo “input text” onde o cliente devesse colocar seu nome, então, depois que ele enviasse a imagem, essa mesma imagem teria o nome dele + a terminação/extensão do arquivo enviado (.jpg, .png, .gif, etc…) Exemplo: Alguém envia uma imagem “SDC00034.jpg” mas a imagem fica salva na minha pasta online como “marcos_andrade_ferreira.jpg”, já que ele preencheu o campo “input text” com o nome “marcos andrade ferreira” e então enviou a imagem.

    Sem dúvida isso facilitaria muito a ligação do nome do cliente que comprou com a imagem que foi enviada.

  9. Olá Rafael parabéns pelo artigo.

    Queria saber como que faço para apenas validar o tamanho do arquivo e e limitar para apenas um arquivo, pois preciso apenas colocar estas regras. Abraço!!

Deixe um Comentário

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

*

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">