2
respostas

Curso de SpringMVC - Gravação de arquivos não funciona

Quando seleciono o arquivo e tento gravar o caminho na base e o arquivo no servidor não é enviado ao Servidor também.

Código do Formulário:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="s"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Livros de Java, Android, iPhone, Ruby, PHP e muito mais - Casa do Código</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
      <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
    <jsp:include page="barra_superior.jspf" />
    <jsp:include page="barra_busca.jspf" />
    <div class="container">
        <div style="color: #0000FF">
            <c:set var="str" value="Olá" />
            <c:set var="ip" value="${pageContext.request.remoteAddr}" />
            <c:choose>
                <c:when test="${pageContext.request.locale eq 'pt_BR'}">
                    <span>${str} Você é um usuário Brazuca! Seu ip é ${ip}</span>
                </c:when>
                <c:otherwise>
                    <span>${str} You're a Foreigner User! Seu ip é ${ip}</span>
                </c:otherwise>
            </c:choose>
        </div>
    </div>
    <!-- Optimizando a URL action do formulario para o Spring encontrar o caminho -->
    <form:form action="${s:mvcUrl('PC#gravar').build() }" method="POST"
        commandName="produto" enctype="multipart/form-data">
        <div class="form-group">
            <label>TÍTULO</label> 
            <!-- input type="text" class="form-control" name="titulo" placeholder="Informe o Título do Livro" -->
            <form:input path="titulo" class="form-control" placeholder="Informe o Título do Livro"/>
            <form:errors path="titulo" />
        </div>
        <div>
            <label class="form-group">DESCRIÇÃO</label>
            <!-- extarea class="form-control" rows="5" name="descricao"></textarea -->
            <form:textarea class="form-control" rows="3" path="descricao" />
            <form:errors path="descricao" />
        </div>
        <div>
            <label class="form-group">NÚMERO DE PÁGINAS</label> 
            <!-- input type="text" class="form-control" name="paginas" placeholder="Informe o número de páginas do Livro" -->
            <form:input path="paginas" class="form-control" placeholder="Informe o número de páginas do Livro"/>
            <form:errors path="paginas" />
        </div>
        <div>
            <label class="form-group">DATA DE LANÇAMENTO</label>
            <form:input path="dataLancamento" class="form-control" placeholder="Informe a Data de Lançamento"/>
            <form:errors path="dataLancamento" />
        </div>

        <!-- A principal vantagem de usar o forEach é que adicionando qualquer outro tipo 
        de preço no enum, automaticamente a nossa JSP vai estar preparada para trabalhar com ele. -->
        <c:forEach items="${tipos}" var="tipoPreco" varStatus="status">
            <div>
                <label class="form-group">${tipoPreco}</label>
                <form:input path="precos[${status.index}].valor" class="form-control" placeholder="Preencha o Preço de acordo com o Tipo"/> 
                <form:hidden path="precos[${status.index}].tipo" class="form-control" value="${tipoPreco}" />
                <!-- input type="text" class="form-control" name="precos[${status.index}].valor" placeholder="Preencha o Preço de acordo com o Tipo"-->
                <!-- input type="hidden" class="form-control" name="precos[${status.index}].tipo" value="${tipoPreco}"-->
            </div>
        </c:forEach>

        <div>
            <label>Sumário</label> 
            <input name="sumario" type="file" />
        </div>

        <div>
            <br>
            <button type="submit" class="btn btn-primary">Cadastrar</button>        
        </div>
    </form:form>
</body>
</html>

Código da Classe FileSaver /* */

package br.com.casadocodigo.loja.infra;

import java.io.File;
import java.io.IOException;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

/**
 * Classe responsavel pela tratamento de arquivos e topicos de infra estrutura.
 */
@Component
public class FileSaver {

    @Autowired
    private HttpServletRequest request;

    public String write(String baseFolder, MultipartFile file) {
        try {
            String homePath = System.getProperty("user.home");
            String baseFolderPath = homePath + "/" + baseFolder;
            String filePath = baseFolderPath + "/" + file.getOriginalFilename();
            file.transferTo(new File(filePath));

            return filePath;

        } catch (IllegalStateException | IOException e) {
            throw new RuntimeException(e);            
        }
    }
}

Código da Classe AppWebConfiguration /* */

package br.com.casadocodigo.loja.conf;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.format.datetime.DateFormatterRegistrar;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import br.com.casadocodigo.loja.controller.HomeController;
import br.com.casadocodigo.loja.daos.ProdutoDAO;
import br.com.casadocodigo.loja.infra.FileSaver;

/**
 * Classe de configuracao do Servlet do SpringMVC.
 * Na classe AppWebConfiguration nós precisamos usar o recurso de Web MVC do SpringMVC.
 * Antes da declaração da classe devemos adicionar a anotação: @EnableWebMvc.
 * @author Julivan Meridius
 */
@EnableWebMvc
@ComponentScan(basePackageClasses={HomeController.class,ProdutoDAO.class, FileSaver.class})
public class AppWebConfiguration {

    /*
     * A anotação @Bean é para que o retorno da chamada deste metódo possa ser gerenciada pelo SpringMVC, 
     * sem ela nossa configuração não funciona
     */
    @Bean
    public InternalResourceViewResolver internalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource rr = new ReloadableResourceBundleMessageSource();
        rr.setBasename("/WEB-INF/message");
        rr.setDefaultEncoding("UTF-8");
        rr.setCacheSeconds(1);
        return rr;
    }

    @Bean 
    public FormattingConversionService mvcConversionService () {
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
        DateFormatterRegistrar formatterRegistrar = new DateFormatterRegistrar();
        formatterRegistrar.setFormatter(new DateFormatter("dd/MM/yyyy"));
        formatterRegistrar.registerFormatters(conversionService);
        return conversionService;
    }

    @Bean
    public MultipartResolver multipartResolver() {
        return new StandardServletMultipartResolver();
    }
}

Código da Classe ServletSpringMVC

/* */

package br.com.casadocodigo.loja.conf;

import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration.Dynamic;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
 * Classe Servlet para interceptar a captura de Requisicoes da Pagina
 * @author Julivan Meridius
 */
public class ServletSpringMVC extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    //-- Indicando que a classe AppWebConfiguration e a Classe JPAConfiguration estao sobrescrevendo o comportamento desse metodo
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] {AppWebConfiguration.class, JPAConfiguration.class};
    }

    @Override
    protected String[] getServletMappings() {
         return new String[] {"/"};
    }

    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        //-- passando informacao para o filter poder atuar corretamente
        //-- MySQL ja usa por default o UTF-8
        filter.setEncoding("UTF-8");
        //-- realizamos a sobrescrita do Filtro para o caracteres UTF-8
        return new Filter[]{filter};
    }

    @Override
    protected void customizeRegistration(Dynamic registration) {
        registration.setMultipartConfig(new MultipartConfigElement(""));        
    }
}

Código da Classe ProdutosController: /* */

package br.com.casadocodigo.loja.controller;

import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import br.com.casadocodigo.loja.daos.ProdutoDAO;
import br.com.casadocodigo.loja.infra.FileSaver;
import br.com.casadocodigo.loja.model.Produto;
import br.com.casadocodigo.loja.model.TipoPreco;
import br.com.casadocodigo.loja.validation.ProdutoValidation;

/**
 * Classe Controller para os produtos novos a serem cadastrados no sistema.
 * @author Julivan Meridius
 */
@Controller
@RequestMapping("/produtos")
public class ProdutosController {

    @Autowired //-- faz o spring injetar o objeto ProdutoDAO
    private ProdutoDAO produtoDao;

    @Autowired
    private FileSaver fileSaver;

    @RequestMapping("/form")
    public ModelAndView form(Produto produto) {
        /**
         * Quando utilizamos o ModelAndView, além retornar uma página, temos a 
         * possibilidade de enviar objetos de qualquer classe para essas páginas. 
         * Em outras palavras, dessa forma somos capazes de exibir, por exemplo, 
         * as informações das nossas classes modelos.
         */
        ModelAndView modelAndView = new ModelAndView("produtos/form");
        modelAndView.addObject("tipos", TipoPreco.values()); //-- Tipos e a chave que ira ser pega na JSP e passamos o nosso ENUM
        return modelAndView;
    }

    //-- always redirect after POST Method
    @RequestMapping(method=RequestMethod.POST)
    public ModelAndView gravar(MultipartFile sumario, @Valid Produto produto, BindingResult result, RedirectAttributes redirectAttributes) {
        //--Imprimindo o caminho do sumario do livro a partir do MultipartFile
        //--System.out.println("CAMINHO DO PATH DO ARQUIVO---> " + sumario.getOriginalFilename());
        if(result.hasErrors()){
            return form(produto);
        }

        String path = fileSaver.write("arquivos-sumario", sumario);    
        produto.setSumarioPath(path);

         System.out.println(sumario.getOriginalFilename());

        //-- grava efetivamente o produto
        produtoDao.gravar(produto);        

        //-- redirect é feito via GET mesmo com POST tendo sido executado nesse ponto
        //-- amntém o atributo de um request para o outro por apenas um requisicao
        redirectAttributes.addFlashAttribute("sucesso", "--> Produto Cadastrado com Sucesso!");

        return new ModelAndView("redirect:produtos");
    }

    @org.springframework.web.bind.annotation.InitBinder
    public void InitBinder(WebDataBinder binder){
        binder.addValidators(new ProdutoValidation());
    }

    @RequestMapping(method=RequestMethod.GET)
    public ModelAndView listar() {
        List<Produto> produtos = produtoDao.listar();
        ModelAndView view = new ModelAndView("produtos/lista");
        view.addObject("produtos", produtos);
        return view;
    }
}

Observação: Já dei clean no tomcat, removi a adicionei o jar do projeto manualmente mas não funciona e também não grava mais nada no banco de dados após as alterações realizadas.

Obrigado.

2 respostas

Olá, acontece algum erro? chega nulo? Só para a gente conseguir identificar o problema.. olhei o código e não vi o erro ainda...

Lembrando que a pasta deve ser criada antes. Outro detalhe importante é que o arquivo não vai ficar dentro do seu projeto no eclipse e sim na instalação do servidor.

Alberto,

O problema é justamente que não recebo nenhuma mensagem de erro no console do Tomcat mas ao tentar anexar o arquivo aparece para ser anexado (via componente File Chooser, no entanto, no momento de gravação dos dados no MySQL o caminho não está sendo gravado. Realizei a limpeza da base para garantir que não seria lixo ou algo nesse sentido. Quanto ao caminho do arquivo na pasta da aplicação no servidor tranquilo.