Aprenda como fazer Upload de imagens usando AJAX e PHP sem Refresh na Página
Hoje vou demonstrar como fazer Upload de imagens usando AJAX e PHP, esse processamento vai ocorrer sem dar refresh na página.
Nesse post vamos usar uma feature que veio no pacote de aprimoramentos do HTML5, a nova API XMLHttpRequest nível 2 (XHR2) .
Como exemplo vamos construir uma página para cadastrar e editar times de futebol, com nome e o escudo (imagem).
Para exibir todos os times cadastrados e seus escudos, vamos construir uma página para listagem, no final do post disponibilizo o link para download do código.
Conhecendo API FormData()
Importante citar, o XHR2 (XMLHttpRequest nível 2) não é HTML5 porém ele foi implementado em conjunto com outras melhorias dessa nova versão do HTML nos navegadores.
O XHR2 trouxe a API FormData() que prove uma maneira de construir conjuntos de chaves/valores para serem enviados usando XMLHttpRequest.
Essa API envia os dados de maneira idêntica ao submit() com dados codificado em “multipart/form-data“, com isso é possível fazer upload de imagens usando requisições AJAX.
Para usar basicamente temos que instanciar um objeto FormData() e opcionalmente podemos passar um parâmetro que seria o formulário HTML:
1 2 3 4 5 |
// Atribui o elemento <form> var formulario = document.getElementById('id_do_seu_fomulario'); // Instância o objeto FormData passando como parâmetro o formulário var formData = new FormData(formulario); |
Também existem métodos para atribuir valores ao objeto FormData():
1 2 3 4 5 6 7 8 9 10 11 |
// Instância o objeto FormData var formData = new FormData(); //Atribui um Arquivo (Upload) formData.append(nome, file, fileName); // Atribui um valor do tipo Blob formData.append(nome, blob, fileName); // Atribui uma String formData.append(nome, string); |
Existem mais métodos e eventos para o objeto FormData(), visite a documentação W3C para conhecer.
Construindo Upload de imagens usando AJAX e PHP
Como citei acima vamos ter apenas 3 páginas HTML (Consulta, Inclusão e Edição), pois o objetivo é demonstrar upload de imagens usando AJAX.
Para deixar o design das páginas mais agradável vamos baixar o framework Materialize que traz classes prontas para estilização.
As instruções PHP para INSERT, UPDATE e UPLOAD ficam centralizadas no script “action.php“.
Script do Banco de Dados
Nesse exemplo teremos um banco de dados “blog” com apenas uma tabela nomeada como “times”, com três campos (id, nome, escudo).
1 2 3 4 5 6 7 8 9 |
CREATE DATABASE IF NOT EXISTS blog; USE blog; CREATE TABLE times( id INT AUTO_INCREMENT PRIMARY KEY, nome VARCHAR(50), escudo VARCHAR(100) ); |
Script JavaScript
No arquivo “custom.js” temos a mágica do upload de imagens usando AJAX!
É nesse script que instanciamos o objeto “FormData” e passamos como parâmetro o formulário com todos os inputs, inclusive a imagem.
O script “custom.js” possui as instruções para envio da requisição AJAX, por isso ele é chamado antes de fechar a tag </body> nos scripts “cadastro.php” e “editar.php“.
custom.js
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
$(document).ready(function($) { // Evento Submit do formulário $('#formulario').submit(function() { // Captura os dados do formulário var formulario = document.getElementById('formulario'); // Instância o FormData passando como parâmetro o formulário var formData = new FormData(formulario); // Envia O FormData através da requisição AJAX $.ajax({ url: "action.php", type: "POST", data: formData, dataType: 'json', processData: false, contentType: false, success: function(retorno){ if (retorno.status == '1'){ $('.mensagem').html(retorno.mensagem); }else{ $('.mensagem').html(retorno.mensagem) } $('.card-panel').removeClass('hide'); } }); return false; }); // Carrega a imagem selecionada no elemento <img> $("input[type=file]").on("change", function(){ var files = !!this.files ? this.files : []; if (!files.length || !window.FileReader) return; if (/^image/.test( files[0].type)){ var reader = new FileReader(); reader.readAsDataURL(files[0]); reader.onload = function(){ $("#imagem").attr('src', this.result); } } }); }); |
Script PHP de Conexão
Abaixo o script de conexão com o banco de dados sempre usando a extensão PDO.
conexao.php
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
<?php /* * Constantes de parâmetros para configuração da conexão */ define('SGBD', 'mysql'); define('HOST', 'localhost'); define('DBNAME', 'blog'); define('CHARSET', 'utf8'); define('USER', 'root'); define('PASSWORD', '11111'); define('SERVER', 'linux'); class conexao { /* * Atributo estático de conexão */ private static $pdo; /* * Escondendo o construtor da classe */ private function __construct() { // } /* * Método estático para retornar uma conexão válida * Verifica se já existe uma instância da conexão, caso não, configura uma nova conexão */ public static function getInstance() { if (!isset(self::$pdo)) { try { $opcoes = array(\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8'); switch (SGBD) : case 'mysql': self::$pdo = new \PDO("mysql:host=" . HOST . "; dbname=" . DBNAME . ";", USER, PASSWORD, $opcoes); break; case 'mssql':{ if(SERVER == 'linux'): self::$pdo = new \PDO("dblib:host=" . HOST . "; database=" . DBNAME . ";", USER, PASSWORD, $opcoes); else: self::$pdo = new \PDO("sqlsrv:server=" . HOST . "; database=" . DBNAME . ";", USER, PASSWORD, $opcoes); endif; break; } case 'postgre': self::$pdo = new \PDO("pgsql:host=" . HOST . "; dbname=" . DBNAME . ";", USER, PASSWORD, $opcoes); break; endswitch; self::$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { print "Erro: " . $e->getMessage(); } } return self::$pdo; } } |
Página de Inclusão
Nessa página temos apenas o código HTML sendo estilizado com Materialize.
cadastrar.php
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<!DOCTYPE html> <html> <head> <title>Cadastro</title> <link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link type="text/css" rel="stylesheet" href="css/materialize.min.css"/> </head> <body> <div class="container"> <h1>Cadastrar Time</h1> <div class="card-panel hide"> <span class="blue-text text-darken-2 mensagem"></span> </div> <form action="" id='formulario'> <input type="hidden" name="acao" value="incluir"> <div class="row"> <div class="input-field center-align col s4"> <img src="img/padrao.gif" id='imagem' height="200" class="z-depth-2"> </div> <div class="file-field input-field col s8"> <div class="btn"> <span>Imagem</span> <input type="file" name="imagem"> </div> <div class="file-path-wrapper"> <input class="file-path" type="text" placeholder="Escudo do Time" required> </div> </div> <div class="input-field center-align col s8"> <input id="nome" name="nome" type="text" required> <label>Nome do Time</label> </div> <a href="index.php" class="waves-effect red btn"><i class="material-icons left">backspace</i> Voltar</a> <button type="submit" id='gravar' class="waves-effect waves-light btn"> <i class="material-icons left">send</i> Gravar </button> </div> </form> </div> <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> <script type="text/javascript" src="js/materialize.min.js"></script> <script type="text/javascript" src="js/custom.js"></script> </body> </html> |
Página de Edição
Na página de edição fazemos uma consulta pelo id do time passado como parâmetro, carregamos os dados no formulário e exibimos a imagem na tag <img>.
editar.php
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
<?php require 'conexao.php'; $conexao = conexao::getInstance(); $id = (isset($_GET['id'])) ? $_GET['id'] : ''; $sql = 'SELECT id, nome, escudo FROM times WHERE id=:id'; $stm = $conexao->prepare($sql); $stm->bindValue(':id', $id); $stm->execute(); $time = $stm->fetch(PDO::FETCH_OBJ); ?> <!DOCTYPE html> <html> <head> <title>Edição</title> <link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link type="text/css" rel="stylesheet" href="css/materialize.min.css"/> </head> <body> <div class="container"> <h1>Editar Time</h1> <div class="card-panel hide"> <span class="blue-grey-text text-darken-4 mensagem"></span> </div> <form action="" id='formulario'> <input type="hidden" name="acao" value="editar"> <input type="hidden" name="escudo_atual" value="<?=$time->escudo?>"> <input type="hidden" name="id" value="<?=$time->id?>"> <div class="row"> <div class="input-field center-align col s4"> <img src="img/<?=$time->escudo?>" id='imagem' height="200" class="z-depth-2"> </div> <div class="file-field input-field col s8"> <div class="btn"> <span>Alterar Imagem</span> <input type="file" name="imagem"> </div> <div class="file-path-wrapper"> <input class="file-path" type="text" placeholder="Escudo do Time"> </div> </div> <div class="input-field center-align col s8"> <input id="nome" name="nome" type="text" required value="<?=$time->nome?>"> <label>Nome do Time</label> </div> <a href="index.php" class="waves-effect red btn"><i class="material-icons left">backspace</i> Voltar</a> <button type="submit" id='gravar' class="waves-effect waves-light btn"> <i class="material-icons left">send</i> Gravar </button> </div> </form> </div> <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> <script type="text/javascript" src="js/materialize.min.js"></script> <script type="text/javascript" src="js/custom.js"></script> </body> </html> |
Página de Consulta
Abaixo temos uma página simples para listagem dos times cadastrados e também seus respectivos escudos.
index.php
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
<?php require 'conexao.php'; $conexao = conexao::getInstance(); $sql = 'SELECT id, nome, escudo FROM times ORDER BY id DESC'; $stm = $conexao->prepare($sql); $stm->execute(); $times = $stm->fetchAll(PDO::FETCH_OBJ); ?> <!DOCTYPE html> <html> <head> <title>Times de Futebol</title> <link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link type="text/css" rel="stylesheet" href="css/materialize.min.css"/> </head> <body> <div class="container"> <div class="row"> <h1>Cadastro de Times</h1> <a href="cadastrar.php" class="waves-effect waves-light btn right"><i class="material-icons left">add</i> Adicionar</a> </div> <?php if(!empty($times)):?> <table class="striped"> <thead> <tr> <th>ESCUDO</th> <th>NOME</th> <th>AÇÃO</th> </tr> </thead> <tbody> <?php foreach($times as $time):?> <tr> <td><img src="img/<?= $time->escudo ?>" height='80' width='80'></td> <td><?= $time->nome ?></td> <td> <a href="editar.php?id=<?=$time->id?>" class="waves-effect waves-light btn"><i class="material-icons left">mode_edit</i> Editar</a> </td> </tr> <?php endforeach; ?> </tbody> </table> <?php else: ?> <div class="row"> <div class="card-panel teal lighten-5"> NÃO EXISTEM TIMES CADASTRADOS! </div> </div> <?php endif; ?> </div> <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> <script type="text/javascript" src="js/materialize.min.js"></script> </body> </html> |
Script PHP para Gravação
Nesse script centralizamos todas a funções PHP, no topo chamamos o script ‘conexao.php‘ para instanciar nosso objeto PDO.
Recebemos os dados via POST, observem que no PHP não muda em nada a forma como são recebidos os campos via AJAX, continuamos trabalhando com $_FILES[] para processar a imagem.
As mensagens de Erro ou Sucesso são retornadas sempre no formato JSON, pois na requisição AJAX foi setado o “DataType: ‘JSON'”.
action.php
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
<?php require 'conexao.php'; $acao = (isset($_POST['acao'])) ? $_POST['acao'] : ''; $id = (isset($_POST['id'])) ? $_POST['id'] : ''; $nome = (isset($_POST['nome'])) ? $_POST['nome'] : ''; $escudo_atual = (isset($_POST['escudo_atual'])) ? $_POST['escudo_atual'] : ''; $conexao = Conexao::getInstance(); $retorno = array(); if ($acao == 'incluir'): $nome_escudo = 'padrao.jpg'; if(isset($_FILES['imagem']) && $_FILES['imagem']['size'] > 0): $extensoes_aceitas = array('bmp' ,'png', 'svg', 'jpeg', 'jpg'); $array_extensoes = explode('.', $_FILES['imagem']['name']); $extensao = strtolower(end($array_extensoes)); // Validamos se a extensão do arquivo é aceita if (array_search($extensao, $extensoes_aceitas) === false): $retorno = array('status' => 0, 'mensagem' => 'Extensão Inválida!'); echo json_encode($retorno); exit(); endif; // Verifica se o upload foi enviado via POST if(is_uploaded_file($_FILES['imagem']['tmp_name'])): // Verifica se o diretório de destino existe, senão existir cria o diretório if(!file_exists("img")): mkdir("img"); endif; // Monta o caminho de destino com o nome do arquivo $nome_escudo = date('dmY') . '_' . $_FILES['imagem']['name']; // Essa função move_uploaded_file() copia e verifica se o arquivo enviado foi copiado com sucesso para o destino if (!move_uploaded_file($_FILES['imagem']['tmp_name'], 'img/'. $nome_escudo)): $retorno = array('status' => 0, 'mensagem' => 'Houve um erro ao gravar arquivo na pasta de destino!'); echo json_encode($retorno); exit(); endif; endif; endif; $sql = 'INSERT INTO times (nome, escudo)VALUES(:nome, :escudo)'; $stm = $conexao->prepare($sql); $stm->bindValue(':nome', $nome); $stm->bindValue(':escudo', $nome_escudo); $retorno = $stm->execute(); if($retorno): $retorno = array('status' => '1', 'mensagem' => 'REGISTRO INSERIDO COM SUCESSO!'); else: $retorno = array('status' => '0', 'mensagem' => 'ERRO AO INSERIR REGISTRO!'); endif; echo json_encode($retorno); endif; if ($acao == 'editar'): if(isset($_FILES['imagem']) && $_FILES['imagem']['size'] > 0): // Verifica se a foto é diferente da padrão, se verdadeiro exclui a foto antiga da pasta if ($escudo_atual <> 'padrao.jpg' && file_exists("img/" . $escudo_atual)): unlink("img/" . $escudo_atual); endif; $extensoes_aceitas = array('bmp' ,'png', 'svg', 'jpeg', 'jpg'); $array_extensoes = explode('.', $_FILES['imagem']['name']); $extensao = strtolower(end($array_extensoes)); // Validamos se a extensão do arquivo é aceita if (array_search($extensao, $extensoes_aceitas) === false): $retorno = array('status' => 0, 'mensagem' => 'Extensão Inválida!'); echo json_encode($retorno); exit(); endif; // Verifica se o upload foi enviado via POST if(is_uploaded_file($_FILES['imagem']['tmp_name'])): // Verifica se o diretório de destino existe, senão existir cria o diretório if(!file_exists("img")): mkdir("img"); endif; // Monta o caminho de destino com o nome do arquivo $nome_escudo = date('dmY') . '_' . $_FILES['imagem']['name']; // Essa função move_uploaded_file() copia e verifica se o arquivo enviado foi copiado com sucesso para o destino if (!move_uploaded_file($_FILES['imagem']['tmp_name'], 'img/'. $nome_escudo)): $retorno = array('status' => 0, 'mensagem' => 'Houve um erro ao gravar arquivo na pasta de destino!'); echo json_encode($retorno); exit(); endif; endif; else: $nome_escudo = $escudo_atual; endif; $sql = 'UPDATE times SET nome=:nome, escudo=:escudo WHERE id=:id'; $stm = $conexao->prepare($sql); $stm->bindValue(':nome', $nome); $stm->bindValue(':escudo', $nome_escudo); $stm->bindValue(':id', $id); $retorno = $stm->execute(); if($retorno): $retorno = array('status' => '1', 'mensagem' => 'REGISTRO EDITADO COM SUCESSO!'); else: $retorno = array('status' => '0', 'mensagem' => 'ERRO AO EDITAR REGISTRO!'); endif; echo json_encode($retorno); endif; |
Resultado Final
Listagem de Times Vazia:
Listagem com Times Cadastrados:
Página para Inclusão de Times, depois de preencher o nome e selecionar a imagem é só clicar em “GRAVAR”, será exibida a mensagem de Sucesso no Topo:
Página para Edição de Times, depois de preencher o nome ou selecionar a imagem é só clicar em “GRAVAR”, será exibida a mensagem de Sucesso no Topo:
O objetivo desse post foi demonstrar como construir um formulário para efetuar upload de imagens usando AJAX e PHP, ligado a um banco de dados no MySQL.
Espero que tenham gostado, até próxima …
Link para download do código Upload usando AJAX (4312 downloads)