2012-03-16 9 views
6

Quindi sto cercando di inviare una richiesta POST multipart/form-data con file di immagine di grandi dimensioni. Non posso pre-convertire il file in array di byte, la mia app si bloccherà con l'eccezione OutOfMemory, quindi devo scrivere il contenuto del file direttamente sul outputstream della connessione. Inoltre, il mio server non supporta la modalità Chunked, quindi devo calcolare la lunghezza del contenuto prima di inviare i dati e utilizzare setFixedLengthStreamingMode della connessione.HTTPURLConnection - POST multipart/form-data con file di grandi dimensioni con FixedLengthStreamingMode

public void createImagePostWithToken(String accessToken, String text, 
     String type, String imagePath) { 

    URL imageUrl = null; 
    String lineEnd = "\r\n"; 
    String twoHyphens = "--"; 

    // generating byte[] boundary here 

    HttpURLConnection conn = null; 
    DataOutputStream outputStream = null; 
    DataInputStream inputStream = null; 

    int bytesRead, bytesAvailable, bufferSize; 
    byte[] buffer; 
    int maxBufferSize = 1*1024*1024; 

    try 
    { 
     long contentLength; 
     int serverResponseCode; 
     String serverResponseMessage; 
     File file = new File(imagePath);    
     FileInputStream fileInputStream = new FileInputStream(file); 
     imageUrl = buildUri("posts").toURL(); 
     conn = (HttpURLConnection)imageUrl.openConnection(); 
     conn.setConnectTimeout(30000); 
     conn.setReadTimeout(30000); 
     conn.setDoOutput(true); 
     conn.setDoInput(true);   
     conn.setRequestMethod("POST"); 
     conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);    

     String stringForLength = new String(); 

     stringForLength += "Content-Type: multipart/form-data;boundary=" + boundary; 

     stringForLength += twoHyphens + boundary + lineEnd + "Content-Disposition: form-data; name=\"access_token\"" + lineEnd; 
     stringForLength += "Content-Type: text/plain;charset=UTF-8" + lineEnd + "Content-Length: " + accessToken.length() + lineEnd + lineEnd; 
     stringForLength += accessToken + lineEnd + twoHyphens + boundary + lineEnd; 

     stringForLength += "Content-Disposition: form-data; name=\"text\"" + lineEnd; 
     stringForLength += "Content-Type: text/plain;charset=UTF-8" + lineEnd + "Content-Length: " + text.length() + lineEnd + lineEnd; 
     stringForLength += text + lineEnd + twoHyphens + boundary + lineEnd; 

     stringForLength += "Content-Disposition: form-data; name=\"type\"" + lineEnd; 
     stringForLength += "Content-Type: text/plain;charset=UTF-8" + lineEnd + "Content-Length: " + type.length() + lineEnd + lineEnd; 
     stringForLength += type + lineEnd + twoHyphens + boundary + lineEnd; 

     stringForLength += twoHyphens + boundary + lineEnd + "Content-Disposition: form-data; name=\"image\"" + lineEnd; 
     stringForLength += "Content-Type: application/octet-stream" + lineEnd + "Content-Length: " + file.length() + lineEnd + lineEnd; 
     stringForLength += lineEnd + twoHyphens + boundary + twoHyphens + lineEnd; 

     int totalLength = stringForLength.length() + (int)file.length();   
     conn.setFixedLengthStreamingMode(totalLength); 


     outputStream = new DataOutputStream(conn.getOutputStream());   
     outputStream.writeBytes(twoHyphens + boundary + lineEnd); 

     // access token 

     outputStream.writeBytes("Content-Disposition: form-data; name=\"access_token\"" + lineEnd); 
     outputStream.writeBytes("Content-Type: text/plain;charset=UTF-8" + lineEnd); 
     outputStream.writeBytes("Content-Length: " + accessToken.length() + lineEnd); 
     outputStream.writeBytes(lineEnd); 
     outputStream.writeBytes(accessToken + lineEnd); 
     outputStream.writeBytes(twoHyphens + boundary + lineEnd); 

     // text 

     outputStream.writeBytes("Content-Disposition: form-data; name=\"text\"" + lineEnd); 
     outputStream.writeBytes("Content-Type: text/plain;charset=UTF-8" + lineEnd); 
     outputStream.writeBytes("Content-Length: " + text.length() + lineEnd); 
     outputStream.writeBytes(lineEnd); 
     outputStream.writeBytes(text + lineEnd); 
     outputStream.writeBytes(twoHyphens + boundary + lineEnd); 

     // type 

     outputStream.writeBytes("Content-Disposition: form-data; name=\"type\"" + lineEnd); 
     outputStream.writeBytes("Content-Type: text/plain;charset=UTF-8" + lineEnd); 
     outputStream.writeBytes("Content-Length: " + type.length() + lineEnd); 
     outputStream.writeBytes(lineEnd); 
     outputStream.writeBytes(type + lineEnd); 
     outputStream.writeBytes(twoHyphens + boundary + lineEnd); 

     // image 

     outputStream.writeBytes(twoHyphens + boundary + lineEnd); 
     outputStream.writeBytes("Content-Disposition: form-data; name=\"image\"" + lineEnd); 
     //outputStream.writeBytes(lineEnd); 
     outputStream.writeBytes("Content-Type: application/octet-stream" + lineEnd); 
     outputStream.writeBytes("Content-Length: " + file.length() + lineEnd); 
     outputStream.writeBytes(lineEnd);   

     bytesAvailable = fileInputStream.available(); 
     bufferSize = Math.min(bytesAvailable, maxBufferSize); 
     buffer = new byte[bufferSize]; 
     // Read file 
     bytesRead = fileInputStream.read(buffer, 0, bufferSize); 

     while (bytesRead > 0) 
     { 
     outputStream.write(buffer, 0, bufferSize);   
     bytesAvailable = fileInputStream.available(); 
     bufferSize = Math.min(bytesAvailable, maxBufferSize); 
     bytesRead = fileInputStream.read(buffer, 0, bufferSize); 
     } 

     outputStream.writeBytes(lineEnd); 
     outputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); 

     Log.d("posttemplate", "connection outputstream size is " + outputStream.size()); 

     // finished with POST request body 


    // Responses from the server (code and message) 
     serverResponseCode = conn.getResponseCode(); 
     serverResponseMessage = conn.getResponseMessage(); 

     Log.d("posttemplate", "server response code "+ serverResponseCode); 
     Log.d("posttemplate", "server response message "+ serverResponseMessage); 

     fileInputStream.close(); 
     conn.disconnect(); 
     outputStream.flush(); 
     outputStream.close(); 


    } catch (MalformedURLException e) 
    { 
     Log.d("posttemplate", "malformed url", e); 
     //TODO: catch exception; 
    } catch (IOException e) 
    { 
     Log.d("posttemplate", "ioexception", e); 
     //TODO: catch exception 
    }   

} 

Purtroppo, le mie app si blocca con IOException a outputStream.close(), e non ho idea del perché:

03-16 13:56:51.035: D/posttemplate(6479): java.io.IOException: unexpected end of stream 
03-16 13:56:51.035: D/posttemplate(6479): at org.apache.harmony.luni.internal.net.www.protocol.http.FixedLengthOutputStream.close(FixedLengthOutputStream.java:57) 
03-16 13:56:51.035: D/posttemplate(6479): at java.io.FilterOutputStream.close(FilterOutputStream.java:66) 
03-16 13:56:51.035: D/posttemplate(6479): at com.futubra.api.impl.PostTemplate.createImagePostWithToken(PostTemplate.java:282) 
03-16 13:56:51.035: D/posttemplate(6479): at com.futubra.FutubraNewPostActivity.createPost(FutubraNewPostActivity.java:128) 
03-16 13:56:51.035: D/posttemplate(6479): at com.futubra.FutubraNewPostActivity_.access$2(FutubraNewPostActivity_.java:1) 
03-16 13:56:51.035: D/posttemplate(6479): at com.futubra.FutubraNewPostActivity_$5.run(FutubraNewPostActivity_.java:141) 
03-16 13:56:51.035: D/posttemplate(6479): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088) 
03-16 13:56:51.035: D/posttemplate(6479): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581) 
03-16 13:56:51.035: D/posttemplate(6479): at java.lang.Thread.run(Thread.java:1019) 

risposta

5

conn.disconnect() dopo outputStream.close()

1

Per vostra informazione, il codice dovrebbe funzionare va bene con la connessione URL HTTP, ma HTTPS attiverà l'errore Memoria esaurita perché c'è un bug in HttpsURLConnectionImpl.java in Android 2.3.4 (verificato sul mio tablet), e questo bug è stato risolto in Android 4.1 (ho controllato la fonte codice).

+0

Mi può puntare a questo bug che si parla? Mi sembra di essere in esecuzione nello stesso problema utilizzando HttpsUrlConnection su Gingerbread. – HungryTux

2

L'intestazione HTTP non fa parte del corpo, quindi non tenerne conto per la lunghezza del corpo.

stringForLength += "Content-Type: multipart/form-data;boundary=" + boundary; 

rimuovere la linea di cui sopra, in modo da lunghezza del contenuto saranno corretti.

0
int totalLength = stringForLength.length() + (int)file.length(); 

Il codice sopra riportato è errato.

Si consiglia di utilizzare la lunghezza in byte della stringa come segue

int totalLength = stringForLength.getBytes().length + (int)file.length(); 
Problemi correlati