Spring - Wysyłanie pliku błąd

0

Witam, mam na zadanie stworzyć aplikację webową opartą na Spring MVC i budowaną w Maven, gdzie przesyłany jest plik i wczytywany do bazy danych.
Dopiero zaczynam przygodę ze Spring, pracuję w Netbeans, stworzyłem prosty projekt w oparciu tutorial ze strony

https://saarapakarinen.wordpress.com/2015/01/11/building-a-basic-spring-3-web-project-netbeans-and-maven/

który w pełni działa jak należy, próbowałem go rozbudować o kolejną stronę do wgrania pliku, w oparciu o oficjalny poradnik Springa

http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-multipart

jednak nie chce to działać, po przejściu na stronę formularza otrzymuję błąd HTTP Status 400 - Required MultipartFile parameter 'file' is not present

Projekt rozbudowałem w następujący sposób:

w folderze jsp dodałem wyslij.jsp gdzie jest formularz do wysłania pliku o treści


<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Wysylanie pliku</title>
    </head>
        
<body>
    <form method="get" action="http://localhost:8084/Hello/application/wyslij" enctype="multipart/form-data">
            <input type="file" name="file"/>
            <input type="submit"/>
        </form>
</body>
    
    
</html>

Stworzyłem osobny kontroler do obsługi wysyłania pliku o nazwie UploadController.java o zawartości

package helloweb;

import java.io.IOException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

@Controller
public class UploadController 
{

    @RequestMapping(value = "wyslij", method = RequestMethod.GET)
    public String handleFormUpload(@RequestParam("file") MultipartFile file) throws IOException 
    {

        if (!file.isEmpty()) {
            byte[] bytes = file.getBytes();
            // store the bytes somewhere
           return "redirect:tak";
       } else 
        {
           return "redirect:nie";
       }
    }

}

plik web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee                               http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <!-- name of the project//-->
    <display-name>HelloProject</display-name>
    <servlet>
        <servlet-name>front-controller</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
     
    <servlet-mapping>
        <servlet-name>front-controller</servlet-name>
        <url-pattern>/application/*</url-pattern>
    </servlet-mapping>
 
    <!-- max time of the session //-->
    
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <!-- default page //-->
    <welcome-file-list>
        <welcome-file>application/wyslij.jsp</welcome-file>
    </welcome-file-list>
</web-app>

i plik front-controller-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd">
     
     <!-- configuration to fetch jsp files automatically from WEB-INF/jsp -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
        <property name="prefix" value="/WEB-INF/jsp/" /> 
        <property name="suffix" value=".jsp" /> 
    </bean>
    
    <bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

    <!-- one of the properties available; the maximum file size in bytes -->
    <property name="maxUploadSize" value="100000"/>

</bean>


    
    <context:component-scan base-package="helloweb" />
   
</beans>   

Czy błąd jest gdzieś w kodzie czy też samej architekturze projektu? Proszę o wyrozumiałość, jeśli czegoś nie rozumiem lub rozumiem błędnie to proszę o wyjaśnienie. Z góry dzięki.

0

No bo po przejściu na tę stronę w przeglądarce, wysyłasz GETa, a jak tworzysz jakiś formularz do wysłania to używaj POSTa. Wtedy w inpucie od file wybierzesz ze swojego kompa jakiś plik i jak nadusisz submit to go wyślesz postem. Ten submit to niech będzie button a nie input.

0

Używam GET, bo gdy zmieniam na POST otrzymuje błąd HTTP Status 405 - Request method 'GET' not supported
gdy szukałem o tym błędzie na jakimś forum doradzili używać GET

0

Najczęściej GET odpowiada za zwykłe pobranie strony lub jakiegoś resourca. POSTa się używa najczęściej do wysłania jakiegoś payloadu np formularza. Najlepiej będzie jak widok z inputami dasz na GET (żebyś po wpisaniu urla w przeglądarce dostał na twarz te inputy), a do przycisku z submitem pdoepniesz akcję do innego urla, z metodą POST i w ten sposób wyślesz ten plik pod ten nowy url.

0

A czy nie dałoby się tego zrobić jakoś prościej? Dopiero zaczynam ze Springiem i nie chce sobie za bardzo skomplikować. Co zrobić żeby działała jedna prosta formatka z wysłaniem pojedynczego pliku?

0

Nie wiem dlaczego moja osobno dodana klasa i formatka nie chciały działać i nawet się wyświetlić, postanowiłem nie mieszać i przerobiłem wcześniej działające pliki obsługujące formularz z tekstem na wysyłanie pliku.

form.jsp wygląda teraz tak:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        
        <title>Form Page</title>
    </head>
    <body>
        
            <form method="POST" enctype="multipart/form-data" action="http://localhost:8084/Hello/application/form">
              <label>file to send: <input type="file" name="file" /></label>
              <input type="submit" />
            </form>
    </body>
</html>

a HelloController tak:


package helloweb;
 
import java.io.IOException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
 
 
@Controller
public class HelloController 
{ 
    @RequestMapping(value = "form", method = RequestMethod.POST)
    public String login(@RequestParam(value = "file", required = true) MultipartFile file) throws IOException 
    {
                    if (!file.isEmpty()) 
                    {
            byte[] bytes = file.getBytes();
            // store the bytes somewhere
           return "redirect:tak";
       } 
                    else 
        {
           return "redirect:nie";
        }
         
    }
    
        @RequestMapping("form")
    public String viewLoginPage(Model model)
    {  
        return "form";
    }
}

Teraz po przejściu na stronę formularza w końcu wyświetla się on, można wybrać plik i kliknąć przycisk, jednak po kliknięciu otrzymuje błąd:

HTTP Status 500 - Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided

EDIT:

Zasugerowano mi usunąć springMultipartFilter z web.xml i wtedy zadziałało. Jednak jest kolejny problem, czy potrzebna jest metoda viewLoginPage? W moim projekcie wydaje się być zbędna, nic nie robi, jest pozostałością po projekcie z tutoriala. Jednak gdy ją usuwam, po przesłaniu pliku otrzymuje błąd:
HTTP Status 405 - Request method 'GET' not supported

Czy ktoś może wyjaśnić?

0

Metoda viewLoginPage zwraca widok - czyli stronę z formularzem - GET na zasobie /form. Metoda login odpowiada za obsłużenie submita formularza - POST na /form.
Jeśli nie podasz w @RequestMapping typu requestu, to domyślnie będzie to GET.

0

Metoda login ma podane w @RequestMapping typ requesta POST, tak samo w formatce z wysyłaniem pliku jest wskazana metoda POST, czy coś źle rozumiem?

1

Tak, ale wchodząc na formularz przez przeglądarkę na adres http://localhost:8084/Hello/application/form wykonujesz żądanie GET, czyli potrzebujesz metody, która to obsłuży - w tym przypadku viewLoginPage.

0

Ano tak, zapomniałem o żądaniu GET po wejściu na adres. Czyli jednak metoda viewLoginPage jest potrzebna i nie mogę jej wyrzucać, obsłuży ona żądanie GET po wpisaniu adresu i za pomocą return "form" wyświetli zawartość formularza zawartego w form.jsp. Następnie żądaniem POST zostanie wysłany plik z formularza, żądanie to zostanie obsłużone przez metodę login, która odbierze ten plik i przekieruje na odpowiednią stronę z wynikiem operacji. Czy mam rację? Czy takie rozwiązanie nada się później do rozwinięcia aplikacji by przesyłać pliki XLS do bazy danych? Czy może nie jest to najlepszy sposób i by trafiło to do bazy danych trzeba w inny sposób to przesłać?

1

Tak jak wyżej podpowiadają czyli musisz napisać 2 metody obsługujące requesta. Pierwsza jako get która w odpowiedzi na request /form wyświetla ci formularz

    
   @RequestMapping(method = RequestMethod.GET, path="form")
    public String viewLoginPage(Model model)
    {  
        return "form";
    }

i druga która w wyniku submitu formularza odbiera ci dane podane w formularzu jako post, którą masz napisaną.
I fajnie by było jakbyś przeniósł sobie path do klasy tak żeby nie pisać tego po n-razy czyli

@Controller
@RequestMapping("/form")
public class HelloController {...}

i później robisz 2 metody

@RequestMapping(method = RequestMethod.GET)
public String viewLoginPage() {...}

oraz

@RequestMapping(method = RequestMethod.POST)
public String processForm(...) {...}
0

Czyli jednak dobrze rozumiem jak to działa. Trochę się chyba zapędziłem w upraszczaniu kodu i usuwaniu zbędnych elementów z tutoriala.

1 użytkowników online, w tym zalogowanych: 0, gości: 1