2012-03-16 21 views
6

Así que estoy tratando de enviar una solicitud POST multipart/form-data con archivo de imagen grande. No puedo convertir previamente la matriz de bytes a archivos, mi aplicación se bloqueará con la excepción OutOfMemory, así que tengo que escribir el contenido del archivo directamente en la salida de la conexión. Además, mi servidor no es compatible con el modo fragmentado, por lo que tengo que calcular la longitud del contenido antes de enviar datos y usar setFixedLengthStreamingMode de la conexión.HTTPURLConnection - POST multipart/form-data con archivo grande 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 
    }   

} 

Por desgracia, mis aplicación se bloquea con IOException en outputStream.close(), y no tengo ni idea de por qué:

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) 

Respuesta

5

conn.disconnect() después outputStream.close()

1

Para su información, el código debe trabajar bien con la conexión URL HTTP, pero HTTPS activará el error Sin memoria porque hay un error en HttpsURLConnectionImpl.java en Android 2.3.4 (verificado en mi tableta), y este error se ha corregido en Android 4.1 (he verificado la fuente código).

+0

¿Me podría apuntar a este error que mencionas? Parece que me estoy encontrando con el mismo problema usando HttpsUrlConnection en Gingerbread. – HungryTux

2

El encabezado HTTP no es parte del cuerpo, por lo que no debe tenerlos en cuenta para la longitud del cuerpo.

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

elimine la línea anterior, por lo que la longitud del contenido será correcta.

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

Su código anterior es incorrecto.

Se debe utilizar la longitud en bytes de la cadena de la siguiente manera

int totalLength = stringForLength.getBytes().length + (int)file.length(); 
Cuestiones relacionadas