12

Ich versuche, eine große Datei mit der "Streaming" Apache Commons File Upload API hochzuladen.SpringBoot: Upload großer Datei-Dateien mit Apache Commons FileUpload

Der Grund, warum ich den Apache Commons File Uploader und nicht den Spring Multipart Uploader benutze, ist, dass er beim Upload sehr großer Dateigrößen (~ 2GB) fehlschlägt. Ich arbeite an einer GIS-Anwendung, wo solche Datei-Uploads ziemlich häufig sind.

Der vollständige Code für meine Datei-Upload-Controller ist wie folgt:

@Controller 
public class FileUploadController { 

    @RequestMapping(value="/upload", method=RequestMethod.POST) 
    public void upload(HttpServletRequest request) { 
     boolean isMultipart = ServletFileUpload.isMultipartContent(request); 
     if (!isMultipart) { 
      // Inform user about invalid request 
      return; 
     } 

     //String filename = request.getParameter("name"); 

     // Create a new file upload handler 
     ServletFileUpload upload = new ServletFileUpload(); 

     // Parse the request 
     try { 
      FileItemIterator iter = upload.getItemIterator(request); 
      while (iter.hasNext()) { 
       FileItemStream item = iter.next(); 
       String name = item.getFieldName(); 
       InputStream stream = item.openStream(); 
       if (item.isFormField()) { 
        System.out.println("Form field " + name + " with value " + Streams.asString(stream) + " detected."); 
       } else { 
        System.out.println("File field " + name + " with file name " + item.getName() + " detected."); 
        // Process the input stream 
        OutputStream out = new FileOutputStream("incoming.gz"); 
        IOUtils.copy(stream, out); 
        stream.close(); 
        out.close(); 

       } 
      } 
     }catch (FileUploadException e){ 
      e.printStackTrace(); 
     }catch (IOException e){ 
      e.printStackTrace(); 
     } 
    } 

    @RequestMapping(value = "/uploader", method = RequestMethod.GET) 
    public ModelAndView uploaderPage() { 
     ModelAndView model = new ModelAndView(); 
     model.setViewName("uploader"); 
     return model; 
    } 

} 

Das Problem ist, dass die getItemIterator(request) immer einen Iterator zurückgibt, die keine Elemente hat (das heißt iter.hasNext()) immer wieder false.

Meine application.properties Datei ist wie folgt:

spring.datasource.driverClassName=org.postgresql.Driver 
spring.datasource.url=jdbc:postgresql://localhost:19095/authdb 
spring.datasource.username=georbis 
spring.datasource.password=asdf123 

logging.level.org.springframework.web=DEBUG 

spring.jpa.hibernate.ddl-auto=update 

multipart.maxFileSize: 128000MB 
multipart.maxRequestSize: 128000MB 

server.port=19091 

Die JSP-Ansicht für die /uploader ist wie folgt:

<html> 
<body> 
<form method="POST" enctype="multipart/form-data" action="/upload"> 
    File to upload: <input type="file" name="file"><br /> 
    Name: <input type="text" name="name"><br /> <br /> 
    Press here to upload the file!<input type="submit" value="Upload"> 
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> 
</form> 
</body> 
</html> 

Was könnte ich falsch gemacht?

+4

Haben Sie Federn mehrteilige Unterstützung deaktiviert sonst Ihre Lösung wird nicht funktionieren und Spring wird bereits die Anforderungen analysiert haben. Ersetzen Sie alle "multipart" -Eigenschaften durch ein einzelnes "multipart.enabled = false", um die Standardbehandlung zu deaktivieren. –

+0

Ich hatte nichts speziell für die Deaktivierung der Spring-Multipart-Unterstützung getan. Ich habe versucht, 'multipart.enabled = false' in meine Datei' application.properties' einzufügen. Aber sobald ich das mache, bekomme ich bei jedem Upload nur eine '405: Request-Methode' POST 'nicht unterstützt. – balajeerc

+0

Dies würde auf eine falsche Zuordnung oder eine falsche URL hinweisen ... Aktivieren Sie die Debug-Protokollierung und sehen Sie, an welche URL Sie senden und an welche URL Ihre Controller-Methode angepasst wurde. –

Antwort

19

Dank einiger sehr hilfreicher Kommentare von M.Deinum konnte ich das Problem lösen. Ich habe einige meiner ursprünglichen Posts bereinigt und posten diese als vollständige Antwort zur späteren Bezugnahme.

Der erste Fehler, den ich machte, war das Deaktivieren der Standardeinstellung MultipartResolver, die Spring bietet. Dies führte dazu, dass der Resolver die HttpServeletRequest verarbeitete und sie somit verbrauchte, bevor mein Controller darauf reagieren konnte.

Die Art und Weise sie zu deaktivieren, dank M. Deinum war wie folgt:

multipart.enabled=false 

Allerdings gibt noch weitere versteckte Gefahr für mich danach wartete. Sobald ich Resolver deaktiviert Standard mehrteiliger, begann ich die folgende Fehlermeldung, wenn ich versuche einen Upload zu machen:

Fri Sep 25 20:23:47 IST 2015 
There was an unexpected error (type=Method Not Allowed, status=405). 
Request method 'POST' not supported 

In meiner Sicherheitskonfiguration hatte ich CSRF-Schutz aktiviert. Die erforderte, dass ich meine POST-Anfrage auf die folgende Weise senden:

<html> 
<body> 
<form method="POST" enctype="multipart/form-data" action="/upload?${_csrf.parameterName}=${_csrf.token}"> 
    <input type="file" name="file"><br> 
    <input type="submit" value="Upload"> 
</form> 
</body> 
</html> 

ich meinen Controller auch ein wenig geändert:

@Controller 
public class FileUploadController { 
    @RequestMapping(value="/upload", method=RequestMethod.POST) 
    public @ResponseBody Response<String> upload(HttpServletRequest request) { 
     try { 
      boolean isMultipart = ServletFileUpload.isMultipartContent(request); 
      if (!isMultipart) { 
       // Inform user about invalid request 
       Response<String> responseObject = new Response<String>(false, "Not a multipart request.", ""); 
       return responseObject; 
      } 

      // Create a new file upload handler 
      ServletFileUpload upload = new ServletFileUpload(); 

      // Parse the request 
      FileItemIterator iter = upload.getItemIterator(request); 
      while (iter.hasNext()) { 
       FileItemStream item = iter.next(); 
       String name = item.getFieldName(); 
       InputStream stream = item.openStream(); 
       if (!item.isFormField()) { 
        String filename = item.getName(); 
        // Process the input stream 
        OutputStream out = new FileOutputStream(filename); 
        IOUtils.copy(stream, out); 
        stream.close(); 
        out.close(); 
       } 
      } 
     } catch (FileUploadException e) { 
      return new Response<String>(false, "File upload error", e.toString()); 
     } catch (IOException e) { 
      return new Response<String>(false, "Internal server IO error", e.toString()); 
     } 

     return new Response<String>(true, "Success", ""); 
    } 

    @RequestMapping(value = "/uploader", method = RequestMethod.GET) 
    public ModelAndView uploaderPage() { 
     ModelAndView model = new ModelAndView(); 
     model.setViewName("uploader"); 
     return model; 
    } 
} 

wo Antwort ist nur ein einfacher generische Antwort Typ I verwenden:

public class Response<T> { 
    /** Boolean indicating if request succeeded **/ 
    private boolean status; 

    /** Message indicating error if any **/ 
    private String message; 

    /** Additional data that is part of this response **/ 
    private T data; 

    public Response(boolean status, String message, T data) { 
     this.status = status; 
     this.message = message; 
     this.data = data; 
    } 

    // Setters and getters 
    ... 
} 
1

Bitte versuchen Sie spring.http.multipart.enabled=false in application.properties Datei hinzuzufügen.

3

Wenn Sie eine neuere Version von Spring-Boot verwenden (ich verwende 2.0.0.M7), dann haben sich die Eigenschaftsnamen geändert. Spring begann unter Verwendung von Technologie-spezifischen Namen

spring.servlet.multipart.maxFileSize = -1

spring.servlet.multipart.maxRequestSize = -1

spring.servlet.multipart.enabled = false

Wenn Sie StreamClosed Ausnahmen sind immer mehrere Implementierungen, die durch aktiv zu sein , dann die letzte Option können Sie die Standard-Feder-Implementierung deaktivieren