2015-04-17 9 views
5

Un po 'di storia prima della mia domanda, ho inserito una domanda sul caricamento di più file su coldfusion usando multipart/form-data di HTML5. E ha funzionato magnificamente. Can you isolate code from being seen from CF10 compiler?Trasferimento file multipart da Java a Coldfusion - nessun limite principale:% PDF-1.4

Il nostro cliente ha finalmente chiesto alcuni test unitari per le funzioni RESTful che ho messo insieme, e sono stato in grado di ottenere un bel po 'fatto, ma ho colpito un posto di blocco con la funzione massUpload che ho progettato sopra.

Siamo spiacenti per la lunga domanda, mettendo giù ciò che è rilevante per il problema.

Ecco il codice in questione:

Unità Codice di prova: la funzione

//Outside class calling sendHTTPrequest 
HashMap<String,String> map = new HashMap<String,String>(); 
HashMap<String,File> getFiles = getFirstFileList(); 
map.put("testMethod", "massUploadTest"); 
map.put("method", "massUpload"); 
map.put("valueString1", valueString1); 
map.put("valueString2", valueString2); 
map.put("valueNumeric3", valueNumeric3); 
map.put("valueBoolean4", valueBoolean4); 
map.put("valueString5", valueString5); 
map.put("valueBoolean6", valueBoolean6); 
map.put("valueString7", valueString7); 

try {      
    sendHTTPrequest(map, getFiles); 
} catch(RuntimeException e) { 
    throw new RuntimeException("Fatal error in massUpload\n" 
      + e.getMessage()); 
} 
//End Call class code 

Coldfusion:

<cffunction name="massUpload" access="remote" returntype="string">  
    <cfargument name="valueString1" type="string" required="false"> 
    <cfargument name="valueString2" type="string" required="false"> 
    <cfargument name="valueNumeric3" type="numeric" required="false" default=0> 
    <cfargument name="valueBoolean4" type="boolean" required="true" default="false"> 
    <cfargument name="valueString5" type="string" required="false"> 
    <cfargument name="valueBoolean6" type="boolean" required="false" default="true"> 
    <cfargument name="valueString7" type="string" required="true"> 

    <!--- massUpload code ---> 
</cffunction> 

Così qui è il codice che sta causando i maggiori problemi. Ho provato diversi approcci per far funzionare il POST, quindi ovviamente il codice ha problemi. Sto provando a farlo senza scaricare più software/librerie, ma se non c'è altro modo, prenderò le disposizioni necessarie. Altrimenti, mi piacerebbe farlo con la libreria standard di Java.

Codice funzione:

//sendHTTPrequest method code 
protected static final String BASE_URI = "<webaddress>/rest.cfc"; 
protected static final String CHARSET = "UTF-8"; 
protected String response; 
protected int status; 
protected String statusMessage; 

protected void sendHTTPrequest(Map<String,String> map, Map<String, File> fileList) { 

     Set<String> keys = map.keySet(); 
     status = 0; 
     response = null; 
     String boundary = "----" + System.currentTimeMillis(); 

     try {    
      URL_CONNECTION = BASE_URL.openConnection(); 
      HTTP_CONNECTION = (HttpURLConnection) (URL_CONNECTION); 

      //Set the request headers 
      HTTP_CONNECTION.setRequestMethod("POST"); 
      URL_CONNECTION.setRequestProperty("Accept-Charset", CHARSET);    
      URL_CONNECTION.setRequestProperty("Content-type", "multipart/form-data; boundary=" + boundary); 

      //Set up a post request 
      URL_CONNECTION.setDoOutput(true); 

      OutputStream output = URL_CONNECTION.getOutputStream(); 
      ByteArrayOutputStream bOutput = new ByteArrayOutputStream(); 
      PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, CHARSET), true); 

      for(String key : keys) { 
       writer.write("--" + boundary); 
       writer.write(lineFeed); 
       writer.write("Content-Disposition: form-data; name=\"" + key + "\""); 
       writer.write(lineFeed); 
       writer.write(lineFeed); 
       writer.write(map.get(key)); 
       writer.write(lineFeed); 
      } 

      FileInputStream inputStream; 
      for(Map.Entry<String, File> entry : fileList.entrySet()){ 
       String name = entry.getKey(); 
       writer.write("--" + boundary); 
       writer.write(lineFeed); 
       writer.write("Content-Disposition: form-data; " 
         + "name=\"allfiles\"; filename=\"" + name + "\""); 
       writer.write(lineFeed); 

       String contentType = URLConnection.guessContentTypeFromName(name); 
       writer.write("Content-Type: " + contentType); 
       writer.write(lineFeed);  

       writer.write("Content-Transfer-Encoding: binary"); 
       writer.write(lineFeed);  

       writer.write("Content-Id: <" + boundary + ">"); 
       writer.write(lineFeed); 
       writer.write(lineFeed); 

       File temp = entry.getValue(); 

       byte[] buffer = FileUtils.readFileToByteArray(temp); 
       output.write(buffer, 0, buffer.length); 
       output.flush(); 

       writer.write(lineFeed);  
      } 

      writer.write("--" + boundary + "--"); 
      writer.write(lineFeed); 
      writer.flush(); 

      writer.close(); 

      status = HTTP_CONNECTION.getResponseCode(); 
      statusMessage = HTTP_CONNECTION.getResponseMessage(); 
      if(status == SUCCESS) { 
       response = IOUtils.toString(URL_CONNECTION.getInputStream(), URL_CONNECTION.getContentEncoding()); 
      } 
      System.out.println("Finished test " + map.get("testMethod") + " Status: " + status); 
      System.out.println("Response: " + response); 
      HTTP_CONNECTION.disconnect(); 

     } catch(UnknownServiceException use) { 
      throw new RuntimeException("Protocol for the output is not supported"); 
     } catch(IOException ioe) { 
      throw new RuntimeException("Unable to create the output stream"); 
     } 
    } 
//End sendHTTPrequest method code 

vedo in uscita per lo stato, la risposta e statusMessage, che ho un errore 500, null, Internal Server Error.

Guardando sul server Coldfusion vedo:

SEVERE: Servlet.service() for servlet [CFCServlet] in context with path [/] threw exception 
java.io.IOException: Corrupt form data: no leading boundary: %PDF-1.4 != ------1429222349902 
at com.oreilly.servlet.multipart.MultipartParser.<init>(MultipartParser.java:182) 
... 

massUpload in grado di gestire diversi tipi di file, ma è alla ricerca specificamente per i PDF. Quindi il test unitario deve essere in grado di inviare diversi tipi di file fino a massloadload, non solo PDF.

Qualsiasi comprensione del problema sarebbe gradita. Grazie.

+0

Hai usato uno sniffer di pacchetti per vedere cosa genera effettivamente quanto sopra? – Leigh

+0

Perché stai scrivendo il file su "output" e non su "writer"? –

+0

Poiché lo scrittore è progettato per il testo e sta scrivendo il file come binario. – Leigh

risposta

2

(Dai commenti)

Hai usato uno sniffer di pacchetti per vedere che cosa realmente genera quanto sopra?

Prova vampate di calore lo scrittore poco prima di scrivere i byte del file:

... 
byte[] buffer = FileUtils.readFileToByteArray(temp); 
writer.flush(); // flush stream here 
output.write(buffer, 0, buffer.length); 
... 

Aggiornamento:

Giusto per chiarire, la ragione per l'errore originale è che anche se il codice crea un PrintWriter con autoFlush=true, non viene ancora salvato automaticamente il testo nel OutputStream sottostante quando viene richiamato write().L'impostazione autoFlush effetto solo questi metodi:

AutoFlush - se true, i println, printf o format metodi saranno [automaticamente] svuotare il buffer di uscita

Di conseguenza, il codice scrive il contenuto del file nello stream prima dello dello boundary markers, che crea un POST HTTP non valido. Da qui l'errore 500. È possibile verificare ciò utilizzando un pacchetto sniffer come Fiddler:

originale - (non valido):

POST http://localhost:8888/test.cfm HTTP/1.1 
Accept-Charset: UTF-8 
Content-Type: multipart/form-data; boundary=14cd4c75e24 
... 
Content-Length: 65570 

%PDF-1.4 
%  
2 0 obj <</Type/XObject/ColorSpace/DeviceGray/Subtype/Image/BitsPerComponent 8/Width 612/Length 11876/Height 792/Filter/FlateDecode>>stream 

... more binary 

--14cd4c75e24 
Content-Disposition: form-data; name="allfiles"; filename="file0.pdf" 
Content-Type: application/pdf 
Content-Transfer-Encoding: binary 
Content-Id: <14cd4c75e24> 


--14cd4c75e24-- 

La soluzione è quella di svuotare lo scrittore prima aggiungere il contenuto del file. Ciò garantisce che i marker di confine siano aggiunti nella posizione corretta, quindi il contenuto POST generato è valido.

nuovo codice (XHTML)

POST http://localhost:8888/test.cfm HTTP/1.1 
Accept-Charset: UTF-8 
Content-Type: multipart/form-data; boundary=14cd4c91d29 
... 
Content-Length: 65570 

--14cd4c91d29 
Content-Disposition: form-data; name="allfiles"; filename="file0.pdf" 
Content-Type: application/pdf 
Content-Transfer-Encoding: binary 
Content-Id: <14cd4c91d29> 

%PDF-1.4 
%  
2 0 obj <</Type/XObject/ColorSpace/DeviceGray/Subtype/Image/BitsPerComponent 8/Width 612/Length 11876/Height 792/Filter/FlateDecode>>stream 
... 

Per inciso, credo Content-Id deve essere univoco. Quindi "boundary" probabilmente non è una buona scelta per il valore Content-Id.