2012-12-03 9 views
7

Ważne: To pytanie jest całkowicie bezużyteczne dla każdej wiosennej wersji wyższej niż 3.0.4 jako kwestii omawianej w tym wątku było fixed w tej wersji dawno temu i nie można go już odtworzyć w kolejnych wersjach Springa.Przesyłanie wielu plików przy użyciu MVC wiosny 3.0.2 po HiddenHttpMethodFilter została włączona


Używam wersji Spring 3.0.2. Muszę przesłać kilka plików za pomocą atrybutu przeglądarkę plików multiple="multiple" takich jak

<input type="file" id="myFile" name="myFile" multiple="multiple"/> 

(a nie za pomocą wielu przeglądarek plików coś jak ten podany przez this answer, to rzeczywiście działa próbowałem).

Mimo że żadna wersja Internet Explorera nie obsługuje tego podejścia, chyba że użyto odpowiedniej wtyczki/widgetu jQuery, nie obchodzi mnie to teraz (ponieważ większość innych przeglądarek obsługuje tę funkcję).

To działa prawidłowo z commons fileupload ale oprócz korzystania RequestMethod.POST i RequestMethod.GET metody, ja też chce skorzystać z innych metod żądania obsługiwane i sugerowane przez wiosnę jak RequestMethod.PUT i RequestMethod.DELETE w swoich właściwych miejscach. Aby tak się stało, skonfigurowałem Spring pod numerem HiddenHttpMethodFilter, który jest zgodny z this question.

ale może przesłać tylko jeden plik na raz, mimo że wybrano wiele plików w przeglądarce plików. W klasie kontrolera Spring metoda jest mapowana w następujący sposób.

@RequestMapping(method={RequestMethod.POST}, value={"admin_side/Temp"}) 
public String onSubmit(@RequestParam("myFile") List<MultipartFile> files, @ModelAttribute("tempBean") TempBean tempBean, BindingResult error, Map model, HttpServletRequest request, HttpServletResponse response) throws IOException, FileUploadException { 
    for (MultipartFile file : files) { 
     System.out.println(file.getOriginalFilename()); 
    } 
} 

Nawet z parametrem żądania @RequestParam("myFile") List<MultipartFile> files który jest List typu MultipartFile (zawsze może mieć tylko jeden plik na raz).


Mogę znaleźć strategię, która prawdopodobnie będzie działać z wieloma plikami on this blog. Przeszedłem przez to ostrożnie.

Rozwiązanie poniżej, sekcja ROZWIĄZANIE 2 - stosowanie surowego REQUEST mówi

Jeśli jednak klient nalega na użyciu tej samej nazwy pól formularza takich jako „akt []” lub "files", a następnie zapełnianie tej nazwy wieloma plikami , potrzebny jest mały hack w następujący sposób:. Jak wspomniano powyżej, Spring 2.5 zgłasza wyjątek, jeśli wykryje tę samą formę wprowadzania nazwy pliku typu więcej niż jeden raz. CommonsFileUploadSupport - klasa, która wyrzuca ten wyjątek nie jest ostateczny i method który wyrzuca wyjątek jest zabezpieczone, przy użyciu cuda dziedziczenia i podklasy jednego może łatwo ustalić/zmienić logikę trochę następująco. Zmieniona przeze mnie zmiana jest dosłownie jednym słowem reprezentującym jedno wywołanie metody , które pozwala nam mieć wiele plików przychodzących pod tą samą nazwą wejściową .

Próbuje zastąpić metodę

protected MultipartParsingResult parseFileItems(List fileItems, String encoding){} 

z klasy abstrakcyjnej CommonsFileUploadSupport poprzez rozszerzenie klasy CommonsMultipartResolver takich jak

package multipartResolver; 

import java.io.UnsupportedEncodingException; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.List; 
import java.util.Map; 
import javax.servlet.ServletContext; 
import org.apache.commons.fileupload.FileItem; 
import org.springframework.util.StringUtils; 
import org.springframework.web.multipart.MultipartException; 
import org.springframework.web.multipart.MultipartFile; 
import org.springframework.web.multipart.commons.CommonsMultipartFile; 
import org.springframework.web.multipart.commons.CommonsMultipartResolver; 

public final class MultiCommonsMultipartResolver extends CommonsMultipartResolver { 

    public MultiCommonsMultipartResolver() {} 

    public MultiCommonsMultipartResolver(ServletContext servletContext) { 
     super(servletContext); 
    } 

    @Override 
    @SuppressWarnings("unchecked") 
    protected MultipartParsingResult parseFileItems(List fileItems, String encoding) { 
     Map<String, MultipartFile> multipartFiles = new HashMap<String, MultipartFile>(); 
     Map multipartParameters = new HashMap(); 

     // Extract multipart files and multipart parameters. 
     for (Iterator it = fileItems.iterator(); it.hasNext();) { 
      FileItem fileItem = (FileItem) it.next(); 

      if (fileItem.isFormField()) { 
       String value = null; 

       if (encoding != null) { 
        try { 
         value = fileItem.getString(encoding); 
        } catch (UnsupportedEncodingException ex) { 
         if (logger.isWarnEnabled()) { 
          logger.warn("Could not decode multipart item '" + fileItem.getFieldName() 
            + "' with encoding '" + encoding + "': using platform default"); 
         } 

         value = fileItem.getString(); 
        } 
       } else { 
        value = fileItem.getString(); 
       } 

       String[] curParam = (String[]) multipartParameters.get(fileItem.getFieldName()); 

       if (curParam == null) { 
        // simple form field 
        multipartParameters.put(fileItem.getFieldName(), new String[]{value}); 
       } else { 
        // array of simple form fields 
        String[] newParam = StringUtils.addStringToArray(curParam, value); 
        multipartParameters.put(fileItem.getFieldName(), newParam); 
       } 
      } else { 
       // multipart file field 
       CommonsMultipartFile file = new CommonsMultipartFile(fileItem); 
       if (multipartFiles.put(fileItem.getName(), file) != null) { 
        throw new MultipartException("Multiple files for field name [" + file.getName() 
          + "] found - not supported by MultipartResolver"); 
       } 

       if (logger.isDebugEnabled()) { 
        logger.debug("Found multipart file [" + file.getName() + "] of size " + file.getSize() 
          + " bytes with original filename [" + file.getOriginalFilename() + "], stored " 
          + file.getStorageDescription()); 
       } 
      } 
     } 

     return new MultipartParsingResult(multipartFiles, multipartParameters); 
    } 
} 

Co się dzieje, że ostatni wiersz w metodzie parseFileItems() (oświadczenie zwrotne), tj.

return new MultipartParsingResult(multipartFiles, multipartParameters); 

powoduje błąd kompilacji, ponieważ pierwszy parametr multipartFiles jest rodzajem Map realizowane przez HashMapale w rzeczywistości, to wymaga parametru typuMultiValueMap<String, MultipartFile>

Jest to konstruktor klasy statycznej wewnątrz abstrakcyjny Klasa CommonsFileUploadSupport,

public abstract class CommonsFileUploadSupport { 
    protected static class MultipartParsingResult { 
     public MultipartParsingResult(MultiValueMap<String, MultipartFile> mpFiles, Map<String, String[]> mpParams) {} 
    } 
} 

powodem może być - to rozwiązanie jest o wiosennej wersji 2.5 i używam wersji 3.0.2 wiosnę, które mogą być nieodpowiednie dla tej wersji.


ja jednak starał się zastąpić Map z MultiValueMap w różnych sposobów, takich jak ten pokazany na poniższym segmencie kodu

MultiValueMap<String, MultipartFile>mul=new LinkedMultiValueMap<String, MultipartFile>(); 

for(Entry<String, MultipartFile>entry:multipartFiles.entrySet()) { 
    mul.add(entry.getKey(), entry.getValue()); 
} 

return new MultipartParsingResult(mul, multipartParameters); 

ale bez powodzenia. Nie jestem pewien, jak zastąpić Map z MultiValueMap i nawet może to działać. Po wykonaniu tej czynności, przeglądarka pokazuje odpowiedź HTTP

HTTP status 400 -

typ raportu Stan

wiadomość

opis Żądanie wysłane przez klienta było składniowo niepoprawny ().

Apache Tomcat/6.0.26


próbowałem skrócić pytanie, jak to możliwe, jak tylko mogłem i nie obejmowały zbędnego kodu.

Jak można umożliwić przesyłanie wielu plików po skonfigurowaniu Spring pod numerem HiddenHttpMethodFilter?

Ten blog wskazuje, że jest to długotrwały błąd o wysokim priorytecie.

Jeśli nie ma rozwiązania dotyczącego wersji 3.0.2 (3 lub wyższej), to muszę wyłączyć wsparcie Spring na zawsze i nadal używać commons-fileupolad, jak sugeruje trzecie rozwiązanie na tym blogu, pomijając PUT, DELETE i inne żądanie metody na zawsze.


Bardzo małe zmiany kodu w metodzie wewnątrz klasy MultiCommonsMultipartResolverparseFileItems() może uczynić go przesłać kilka plików, ale nie może się udać w moich próbach (znów z wiosennej wersji 3.0.2 (lub wyższej) 3).

+0

Naprawiono problem [problem Jira] (https://jira.springsource.org/browse/SPR-2784?page=com.atlassian.jira.plugin.system.issuetabpanels%25253Aall-tabpanel) w poście na blogu od wersji 3.0.4, czy możesz spróbować uaktualnić? –

+0

@RC - Dzięki za ten link. Mam dużą aplikację, która jest więcej niż w połowie ukończona. Zmiana struktury może wymagać zmiany kodu w wielu miejscach, więc mogę wymyślić wyższą wersję tylko dla przyszłych projektów (powinienem był to wiedzieć zanim zacząłem). Dziękuję Ci. – Tiny

+2

3.0.2 do 3.0.4 to niewielkie ulepszenie (głównie poprawki błędów, jeśli spojrzę na numer wersji), więc powinno być bezbolesne. Powinieneś go wypróbować i uruchomić, jeśli działa –

Odpowiedz

1

Kwestia wymieniona w pytaniu to fixed od wiosny 3.0.4. W związku z tym, jeśli używasz tej wersji lub wyższej (tak, teraz jest 4.x.x.), nie musisz już więcej czytać tego pytania/odpowiedzi.

3

do przesyłania wielu plików w jednym żądaniu Użyłem tego kodu:

mam taki jsp:

<p>Select files to upload. Press Add button to add more file inputs.</p> 
<table> 
    <tr> 
     <td><input name="files" type="file" multiple="true"/></td> 
    </tr> 
    <tr> 
     <td><input name="files" type="file" multiple="true"/></td> 
    </tr> 
</table> 
<br/><input type="submit" value="Upload" /> 

plik klasy przesyłanie Fasola:

import org.springframework.web.multipart.commons.CommonsMultipartFile; 

public class FileUploadForm { 

    private CommonsMultipartFile [] files; 

    public CommonsMultipartFile[] getFiles() { 
     return files; 
    } 

    public void setFiles(CommonsMultipartFile[] files) { 
     this.files = files; 
    } 
} 

Kontroler:

@Controller 
@RequestMapping("/upload") 
public class FileUploadController { 

    @RequestMapping(method = RequestMethod.GET) 
    public String displayForm(ModelMap modelMap) { 
     modelMap.addAttribute(new FileUploadForm()); 
     return "uploadForm.jsp"; 
    } 

    @RequestMapping(method = RequestMethod.POST) 
    public String save(FileUploadForm uploadForm) { 
     CommonsMultipartFile[] files = uploadForm.getFiles(); 
     if(files != null && files.length != 0) { 
      for(MultipartFile file : files) { 
       System.out.println(file.getOriginalFilename()); 
      } 
     } 
     return "success.jsp"; 
    } 
} 

Kod ten pozwala na przesyłanie wielu plików w jednym żądaniu, i być możliwe, aby uzyskać instancję CommonsMultipartFile dla każdego pliku.

+0

Tak, to jest sposób przesyłania wielu plików, a technika, której użyłem do przesłania wielu plików, nie była już inna niż ta, którą pokazałeś, ale nie działa z ** wersją Spring 3.0.2 (lub niższą) * * którego używałem wcześniej. W którym znajduje się jeden plik (zwykle pierwszy z listy plików wybranych z przeglądania plików). Czy mówisz o tej wersji? Czy to działa dla ciebie w tej wersji? Jestem pewien, że nie powinno się stało z tą wersją Spring, prawda? Odpowiedź jest najbardziej pożądana. Dziękuję Ci. – Tiny