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.
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.
Espero que vocês tenham entendido, até a próxima.
Abraços.
como faz para gravar a imagem no banco de dados?
Olá Rafa,
Meu objetivo nesse artigo foi apenas apresentar um upload dinâmico.
Peço que você dê uma olhada nesses outros artigos:
Upload simples de imagem com PHP/MySQL
Upload de vários arquivos com PHP
Ok? Abraço.
Show de bola mais uma vez Rafael, parabens e obrigado!
Como não obtive sucesso de outras maneiras, vou tentar implementar seu método.
Qlqr duvida posto aqui ok.
Abraço!
Perfeito cara! Funcionou aqui blzinha. Só que, como eu tinha que enviar o anexo por email, apanhei um pouco! hehehehehe
Mto obrigado mais uma vez!
Gostei muito, só que no meu caso preciso que tenha um outro campo pro cara nomear o documento… ele vai digitar o titulo que ele quer sem renomear o arquivo.
Parabéns cara! ficou muito bom o tutorial
há tempos que eu queria aprender isso e não achava em nenhum lugar
valew!
Olá.
Gostaria de saber se tem como saber o caminho do arquivo no próprio cliente mesmo (C:/Desktop/arquivo.txt)? Com o input file no IE ele popula o campo com o caminho, mas não estou conseguindo com esta API.
Desde já agradeço.
Como faço para adicionar varios campos no form e fazer o upload dinamico para um banco de dados, ou seja, em quais linha do javascript tenho q adicionar as variaveis ou campos??
obrigado
Perfeito, mas para ficar mais perfeito ainda seria legal colocar uma barra de progresso real do up do arquivo (algo com porcentagem).
Alguma ideia para implantar isso?
Muito bom o blog e muito bom este tutorial, bem explicado, escrito e muito útil. Obrigado.
Tentei modificar para limitar o número de arquivos enviados para no máximo 1 mas não deu certo, tentei mexer no e , não entendi se seria por ali. Poderia me indicar onde pode ser alterado para limitar o número máximo de anexos ? Obrigado.
Olá! Ótimo o tutorial, mas se seu quiser criar (miniaturas) e também enviar a informação pelo fomulário, como seria?
Lindo! hahuah
Mto bom, adorei seu post e sera mto util em meu projeto.
Vlw!
Meu herói, valeu cara
Abraço
BOM DEMAIS DA CONTA, PARABÉNS É POUCO,
ESTÁ MUITO BOM. SHOWWWW.
Boa tarde Rafael,
Poderia me ajudar a recuperar a $_POST['anexos'] quando vou fazer o envio dos dados por $.ajax jquery na index? e não através do form upload da index.
Grande abraço
Rafael,
Como faço para limitar para um arquivo por vez… Preciso enviar somente uma imagem e depois desabilitar a opção de inserir mais imagens.
abraço
Marcos
Olá Marcos,
Um jeito simples seria fazer com javascript mesmo, por exemplo:
Porém, lembre-se que javascript é lado cliente, sendo assim há possibilidade de ser burlado, portanto se quiser algo mais seguro recomendo uma validação no lado servidor, ok?
Abraço.
Olá!
Parabéns pelos tutoriais!! Td muito bom!
Mas vc pode me ajudar? Para salvar no servidor com o IP da máquina do usuário ( no caso de uma intranet), como faço?
Olá Cristiele,
Bom, para exibir o IP do usuário basta utilizar:
No caso, o ideal seria que além do arquivo ser enviado para o servidor, ser inserido em um banco de dados também, por que assim poderiamos, por exemplo, relacionar o arquivo (através de seu nome único) com um usuário, e assim teriamos uma melhor organização.
Mas você poderia também criar uma pasta para cada IP, por exemplo:
Abraços.
olá rafael paRABÉNS PELA EXPLICAÇÃO ME AJUDOU MUITO
show de bola seu sistema.. muito bom mesmo…
so uma pergunta.. ao invez da imagem ficar rodando la na hora do upload…
tem como colocar uma barra com % pra ver o progresso do upload ?
Obrigado por disponibilizar seu trabalho.
Olá Henrique,
Possível eu sei que é, mas nunca fiz algo assim, só sei que é muito mais trabalhoso do que colocar um simples .gif, rs.
De qualquer forma é uma ótima ideia para um artigo, hum.
Obrigado, abraço.
Boa dia Rafael,
Seguinte, queria teriar uma duvida, se puder me ajudar eu agradeço.
eu quero salvar os nomes dos arquivos enviados no banco de dados, colocando tudo em apenas um registro em uma coluna… tudo separado por ‘,’… ex.: “arquivo1.jpg, arquivo2.jpg” …
mas na index.php vou criar o botao ENVIAR ai sim ele salva todo o upload realizado.
nao sei se fui claro :S
Obrigado =]
Boa tarde Rafael,
Como ficaria esse tutorial se fosse para enviar arquivos vinculados. Exemplo: Tenho quatro arquivos, sendo selecionado um deles, o upload deve levar todos para o servidor.
if (isset($_POST['enviar']) && $_POST['enviar'] == ‘send’) {
$arquivo = strip_tags(trim($_POST['arquivo']));
//Identifica o nome base do arquivo
$string = $_FILES['arquivo']['name'];
//echo “$string”;
ereg (“(.*)\.([^.]+)$”, $string, $partes);
$base=substr($partes[1], 0,-2); // separa o nome base do arquivo
//echo “Nome base do arquivo é -> $base”;
$ext=($partes[2]); //identifica a extensão do arquivo
//echo “Extensão = $partes[2]“;
$ni=”NI.”;
$nc=”NC.”;
$nd=”ND.”;
$nm=”NM.”;
$arquivo1=$base.$nc.$ext;
$arquivo2=$base.$nm.$ext;
$arquivo3=$base.$ni.$ext;
$arquivo4=$base.$nd.$ext;
* Até aqui o script funciona. Falta agora enviar os arquivos para o $_POST.
Como eu poderia fazer isso?
if (empty($arquivo)) {
die(“Não foi possivel enviar seu arquivo.”);
exit;
}}
?>
excelente! Porém, aparece este erro:
Warning: move_uploaded_file(uploads/) [function.move-uploaded-file]: failed to open stream: Is a directory in /home/amazonjo/public_html/upload/upload.php on line 22
Se alguem souber onde estou errando eu aceito uma ajudinha….
abraços…
Olá Nei Junior,
O erro está dizendo que você está tentando fazer o upload em uma pasta (uploads/), ou seja, você deve especificar o nome do arquivo também (uploads/arquivo.doc, por exemplo), assim como está no artigo:
Perceba que ele concatena $caminho com $nomeAleatorio.
Abraço.