2016-06-16 36 views
6

L'interfaccia MultiPartResolver predefinita di avvio Spring gestisce l'upload di file multipart memorizzandoli nel file system locale. Prima di immettere il metodo del controller, l'intero file multipart deve terminare il caricamento sul server.Spring - Come eseguire lo streaming di grandi caricamenti di file multipart al database senza archiviare sul file system locale

Stiamo salvando tutti i nostri file caricati direttamente in un database ei nostri server hanno una quota disco molto piccola, quindi se un file di grandi dimensioni viene caricato, stiamo vedendo uno IOExeption - Disk quota exceeded.

C'è un modo per ottenere lo stream direttamente dalla richiesta in arrivo del client prima che Spring's MultiPartResolver memorizzi il file sul filesystem locale in modo che possiamo trasmettere direttamente al nostro db?

+0

cosa del database stai usando? – Songo

+0

Stiamo usando AWS S3 – McLovin

risposta

5

È possibile utilizzare direttamente l'apache, come descritto qui https://commons.apache.org/proper/commons-fileupload/streaming.html.

@Controller 
public class UploadController { 

    @RequestMapping("/upload") 
    public String upload(HttpServletRequest request) throws IOException, FileUploadException { 

     ServletFileUpload upload = new ServletFileUpload(); 

     FileItemIterator iterator = upload.getItemIterator(request); 
     while (iterator.hasNext()) { 
      FileItemStream item = iterator.next(); 

      if (!item.isFormField()) { 
       InputStream inputStream = item.openStream(); 
       //... 
      } 
     } 
    } 
} 

Assicurarsi di disabilitare il meccanismo di risoluzione multiparte molle.

application.yml:

spring: 
    http: 
     multipart: 
     enabled: false 
4

In realtà non è un compito banale. Se si desidera scrivere lo stream dal diritto del client al database, è necessario elaborare la richiesta manualmente. Ci sono alcune librerie che possono semplificare questo compito. Uno di questi è "Apache Commons FileUpload". Di seguito un esempio molto semplice, come è possibile elaborare la richiesta in arrivo multipart/form-data da questa libreria.

@Controller 
public class Controller{ 

    @RequestMapping("/upload") 
    public String upload(HttpServletRequest request){ 

     String boundary = extractBoundary(request); 

     try { 
      MultipartStream multipartStream = new MultipartStream(request.getInputStream(), 
       boundary.getBytes(), 1024, null); 
      boolean nextPart = multipartStream.skipPreamble(); 
      while(nextPart) { 
       String header = multipartStream.readHeaders(); 

       if(header.contains("filename")){ 
        //if input is file 
        OutputStream output = createDbOutputStream(); 
        multipartStream.readBodyData(output); 
        output.flush(); 
        output.close(); 
       } else { 
        //if input is not file (text, checkbox etc) 
        ByteArrayOutputStream output = new ByteArrayOutputStream(); 
        multipartStream.readBodyData(output); 
        String value = output.toString("utf-8"); 
        //... do something with extracted value 
       } 
       nextPart = multipartStream.readBoundary(); 
      } 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     }  
    } 

    private String extractBoundary(HttpServletRequest request) { 
     String boundaryHeader = "boundary="; 
     int i = request.getContentType().indexOf(boundaryHeader)+ 
      boundaryHeader.length(); 
     return request.getContentType().substring(i); 
    }  
} 

intestazione per il campo di file volontà assomiglia:

Content-Disposition: form-data; name="fieldName"; filename="fileName.jpg" 
Content-Type: image/jpeg 

Testata per semplice volontà campo assomiglia:

Content-Disposition: form-data; name="fieldName"; 

Nota, che questo frammento è solo semplificato esempio per mostrare la direzione . Non ci sono dettagli come: estrarre il nome del campo dall'intestazione, creare il flusso di output del database, ecc. Puoi implementare tutto questo da solo. Esempi di intestazione di campo della richiesta multipart è disponibile in RFC1867. Informazioni su multipart/form-dataRFC2388.

+0

Ho capito che funziona principalmente con l'intestazione '" Content-Type: multipart/form-data; boundary = ---- WebKitFormBoundary7MA4YWxkTrZu0gW "-F" nomefile = test "-F" file = @ myimage. jpg "' – McLovin