2015-09-04 27 views
8

Provare a utilizzare il plugin di caricamento file blueimpJQuery per caricare file di grandi dimensioni (superiori a 1 GB). Trovato utilizzando la configurazione maxChunkSize consente di eseguire il caricamento del file in blocchi dal lato client. Server possiamo ottenere il numero di file & nome file utilizzando Content-Range & Content-Disposition intestazioni.Caricamento di file in blocchi utilizzando il plug-in di caricamento del file jquery in JAVA

Il mio server è Weblogic e scrive codice lato server in un Servlet.

Ecco le mie domande: lato

  1. Server: Come sapere che la richiesta è l'ultimo pezzo o no?
  2. Lato server Come scrivere tutti i dati dei blocchi ricevuti in un unico file?
  3. Come posso identificare le richieste Chunked relative allo stesso file, poiché ogni blocco verrà inviato come una singola richiesta?
+0

Non dovrebbe essere contrassegnato con "javascript"? –

+0

Vedere http://stackoverflow.com/q/31222657/2801559 – guest271314

+0

le richieste di upload appartengono a una sessione lato server? – wero

risposta

4

Controlla la wiki del plug-in su github - ha una sezione sui caricamenti di file chunked.

Dal wiki:

L'esempio PHP upload handler supporti chunked arrivi fuori dalla scatola .

Per sostenere arrivi chunked, il gestore di upload si avvale della Content-Range header, che viene trasmesso dal plugin per ogni pezzo.

Controllare il codice PHP di esempio collegato sopra.

Lato server: come sapere che la richiesta è l'ultimo pezzo o no?

Ogni richiesta intestazione Content-Range conterrà la gamma di byte del file contenute in tale richiesta, così come il numero totale di byte del file. Pertanto, è possibile controllare il valore finale dell'intervallo rispetto al numero totale di byte per determinare se la richiesta contiene l'ultimo blocco o meno.

Controllare l'esempio fornito nella sezione this sul sito Web del W3C.

Lato server Come scrivere tutti i dati dei blocchi ricevuti in un unico file?

è possibile raccogliere tutti i pezzi in memoria di un array e poi li scrive in un file in un go - ma questo può essere inefficiente per file di dimensioni maggiori. L'API IO di Java fornisce metodi per scrivere su sezioni di file fornendo un offset iniziale. Controlla la domanda this.

Come posso identificare le richieste Chunked relative allo stesso file, poiché ogni blocco verrà inviato come una singola richiesta?

Verificare l'intestazione Content-Range in ogni richiesta: se una richiesta ha quell'intestazione, si tratta di una delle numerose richieste di caricamento di blocchi. Usando il valore dell'intestazione, puoi capire quale parte/sezione del file è contenuta in quella richiesta.

Inoltre, l'intestazione Content-Disposition nella richiesta conterrà il nome del file mediante il quale è possibile collegare varie richieste dello stesso file.

2

Posso rispondere tramite codice di lavoro? Qui ci sono parti client e server necessarie. Vedi la spiegazione di seguito.

Cliente:

<input id="fileupload" type="file" name="files[]" data-url="file.upload" multiple> 
<script> 
var uuid = function() { 
      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, 
       function(c) { 
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 
        return v.toString(16); 
       }); 
     }; 
$(function() { 
    $('#fileupload').fileupload({ 
     dataType: 'json', 
     maxChunkSize: 1000000, 
     done: function (e, data) { 
      $.each(data.result.files, function (index, file) { 
       $('<p/>').text(file.name).appendTo(document.body); 
      }); 
     } 
    }).bind('fileuploadsubmit', function (e, data) { 
     data.formData = {uploadId: uuid()}; 
    }); 
}); 
</script> 

WEB_INF/web.xml:

<!-- ...other servlet blocks... --> 

<servlet> 
    <servlet-name>fileUploadServlet</servlet-name> 
    <servlet-class>your.package.FileUploadServlet</servlet-class> 
</servlet> 

<!-- ...other servlet-mapping blocks... --> 

<servlet-mapping> 
    <servlet-name>fileUploadServlet</servlet-name> 
    <url-pattern>/file.upload</url-pattern> 
</servlet-mapping> 

Servlet "FileUploadServlet":

import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.util.ArrayList; 
import java.util.Iterator; 
import java.util.LinkedHashMap; 
import java.util.List; 
import java.util.Map; 
import java.util.TreeMap; 
import java.util.regex.Pattern; 

import javax.servlet.ServletException; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

import org.apache.commons.fileupload.FileItem; 
import org.apache.commons.fileupload.FileItemFactory; 
import org.apache.commons.fileupload.disk.DiskFileItemFactory; 
import org.apache.commons.fileupload.servlet.ServletFileUpload; 

import com.fasterxml.jackson.databind.ObjectMapper; 

... 

@Override 
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
    String range = request.getHeader("Content-Range"); 
    long fileFullLength = -1; 
    long chunkFrom = -1; 
    long chunkTo = -1; 
    if (range != null) { 
     if (!range.startsWith("bytes ")) 
      throw new ServletException("Unexpected range format: " + range); 
     String[] fromToAndLength = range.substring(6).split(Pattern.quote("/")); 
     fileFullLength = Long.parseLong(fromToAndLength[1]); 
     String[] fromAndTo = fromToAndLength[0].split(Pattern.quote("-")); 
     chunkFrom = Long.parseLong(fromAndTo[0]); 
     chunkTo = Long.parseLong(fromAndTo[1]); 
    } 
    File tempDir = new File(System.getProperty("java.io.tmpdir")); // Configure according 
    File storageDir = tempDir;          // project server environment. 
    String uploadId = null; 
    FileItemFactory factory = new DiskFileItemFactory(10000000, tempDir); 
    ServletFileUpload upload = new ServletFileUpload(factory); 
    try { 
     List<?> items = upload.parseRequest(request); 
     Iterator<?> it = items.iterator(); 
     List<Map<String, Object>> ret = new ArrayList<Map<String,Object>>(); 
     while (it.hasNext()) { 
      FileItem item = (FileItem) it.next(); 
      if (item.isFormField()) { 
       if (item.getFieldName().equals("uploadId")) 
         uploadId = item.getString(); 
      } else { 
       Map<String, Object> fileInfo = new LinkedHashMap<String, Object>(); 
       File assembledFile = null; 
       fileInfo.put("name", item.getName()); 
       fileInfo.put("type", item.getContentType()); 
       File dir = new File(storageDir, uploadId); 
       if (!dir.exists()) 
        dir.mkdir(); 
       if (fileFullLength < 0) { // File is not chunked 
        fileInfo.put("size", item.getSize()); 
        assembledFile = new File(dir, item.getName()); 
        item.write(assembledFile); 
       } else { // File is chunked 
        byte[] bytes = item.get(); 
        if (chunkFrom + bytes.length != chunkTo + 1) 
         throw new ServletException("Unexpected length of chunk: " + bytes.length + 
           " != " + (chunkTo + 1) + " - " + chunkFrom); 
        saveChunk(dir, item.getName(), chunkFrom, bytes, fileFullLength); 
        TreeMap<Long, Long> chunkStartsToLengths = getChunkStartsToLengths(dir, item.getName()); 
        long lengthSoFar = getCommonLength(chunkStartsToLengths); 
        fileInfo.put("size", lengthSoFar); 
        if (lengthSoFar == fileFullLength) { 
         assembledFile = assembleAndDeleteChunks(dir, item.getName(), 
           new ArrayList<Long>(chunkStartsToLengths.keySet())); 
        } 
       } 
       if (assembledFile != null) { 
        fileInfo.put("complete", true); 
        fileInfo.put("serverPath", assembledFile.getAbsolutePath()); 
        // Here you can do something with fully assembled file. 
       } 
       ret.add(fileInfo); 
      } 
     } 
     Map<String, Object> filesInfo = new LinkedHashMap<String, Object>(); 
     filesInfo.put("files", ret); 
     response.setContentType("application/json"); 
     response.getWriter().write(new ObjectMapper().writeValueAsString(filesInfo)); 
     response.getWriter().close(); 
    } catch (ServletException ex) { 
     throw ex; 
    } catch (Exception ex) { 
     ex.printStackTrace(); 
     throw new ServletException(ex); 
    } 
} 

private static void saveChunk(File dir, String fileName, 
     long from, byte[] bytes, long fileFullLength) throws IOException { 
    File target = new File(dir, fileName + "." + from + ".chunk"); 
    OutputStream os = new FileOutputStream(target); 
    try { 
     os.write(bytes); 
    } finally { 
     os.close(); 
    } 
} 

private static TreeMap<Long, Long> getChunkStartsToLengths(File dir, 
     String fileName) throws IOException { 
    TreeMap<Long, Long> chunkStartsToLengths = new TreeMap<Long, Long>(); 
    for (File f : dir.listFiles()) { 
     String chunkFileName = f.getName(); 
     if (chunkFileName.startsWith(fileName + ".") && 
       chunkFileName.endsWith(".chunk")) { 
      chunkStartsToLengths.put(Long.parseLong(chunkFileName.substring(
        fileName.length() + 1, chunkFileName.length() - 6)), f.length()); 
     } 
    } 
    return chunkStartsToLengths; 
} 

private static long getCommonLength(TreeMap<Long, Long> chunkStartsToLengths) { 
    long ret = 0; 
    for (long len : chunkStartsToLengths.values()) 
     ret += len; 
    return ret; 
} 

private static File assembleAndDeleteChunks(File dir, String fileName, 
     List<Long> chunkStarts) throws IOException { 
    File assembledFile = new File(dir, fileName); 
    if (assembledFile.exists()) // In case chunks come in concurrent way 
     return assembledFile; 
    OutputStream assembledOs = new FileOutputStream(assembledFile); 
    byte[] buf = new byte[100000]; 
    try { 
     for (long chunkFrom : chunkStarts) { 
      File chunkFile = new File(dir, fileName + "." + chunkFrom + ".chunk"); 
      InputStream is = new FileInputStream(chunkFile); 
      try { 
       while (true) { 
        int r = is.read(buf); 
        if (r == -1) 
         break; 
        if (r > 0) 
         assembledOs.write(buf, 0, r); 
       } 
      } finally { 
       is.close(); 
      } 
      chunkFile.delete(); 
     } 
    } finally { 
     assembledOs.close(); 
    } 
    return assembledFile; 
} 

L'idea è quella di scrivere pezzi come file separati e da assemblare nel momento in cui tutti i file di blocchi hanno una lunghezza comune uguale alla lunghezza del file completo. Sul lato client è possibile definire le proprietà di utilizzo all'avvio del caricamento (è fatto qui per uploadId - ID univoco per ogni file). Questo uploadId è lo stesso per tutti i blocchi di un file. Qualsiasi domanda? Fammelo sapere.

[AGGIORNAMENTO] Esistono due dipendenze: https://commons.apache.org/proper/commons-fileupload/ e https://github.com/FasterXML/jackson.