Cálculo del tamaño de Post Content-Length para el archivo codificado en Base64

Estoy intentando subir algunos archivos grandes desde un dispositivo Android a un servicio Web .Net. Este servicio web se ha configurado para que acepte estos archivos como un parámetro POST y que los archivos tienen que ser enviados como una cadena codificada en Base64.

He podido usar esta biblioteca de Christian d'Heureuse para convertir el archivo en una cadena Base64, calcular el tamaño de la cadena en bytes y enviarla previamente, sin embargo el método que usé antes implicaba cargar todo el archivo en la memoria que Estaba causando errores de memoria al tratar archivos grandes, lo que no era inesperado.

He estado tratando de convertir el archivo en Base64 en trozos y transmitir estos datos a través de la conexión (utilizando el objeto de flujo de salida de datos) como se está convirtiendo, por lo que el archivo completo no necesita ser cargado en la memoria de una sola vez, sin embargo Parece que no puedo calcular con precisión el tamaño de la Content-Length para la solicitud antes de convertir el archivo – por lo general parecen ser unos 10 bytes – frustrante, que ocasionalmente funciona!

También he encontrado que algo del tiempo cuando esto trabaja el servidor devuelve el mensaje de error siguiente "Tamaño no válido para una matriz de Char de Base64". Creo que esto es un problema con los caracteres de relleno, sin embargo no puedo ver un problema con mi código funciona esto, algunos consejos sobre este tema sería muy apreciado!

Este es el código que genera la solicitud y transmite los datos:

try { HttpURLConnection connection = null; DataOutputStream outputStream = null; DataInputStream inputStream = null; //This is the path to the file String pathToOurFile = Environment .getExternalStorageDirectory().getPath() + "/path/to/the/file.zip"; String urlServer = "https://www.someserver.com/somewebservice/"; int bytesRead, bytesAvailable, bufferSize; byte[] buffer; int maxBufferSize = 456; //The parameters of the POST request - File Data is the file in question as a Base64 String String params = "Username=foo&Password=bar&FileData="; File sizeCheck = new File(pathToOurFile); Integer zipSize = (int) sizeCheck.length(); Integer paddingRequired = ((zipSize * 8) / 6) % 3; Integer base64ZipSize = ((zipSize * 8) / 6) + ((zipSize * 8) / 6) % 3; Integer paramLength = params.getBytes().length; //Code to work out the number of lines required, assuming we create a new //line every 76 characters - this is used t work out the number of //extra bytes required for new line characters Integer numberOfLines = base64ZipSize / 76; Log.i(TAG, "numberOfLines: " + numberOfLines); Integer newLineLength = System.getProperty("line.separator") .getBytes().length; //This works out the total length of the Content Integer totalLength = paramLength + base64ZipSize + (numberOfLines * newLineLength) + paddingRequired; Log.i(TAG, "total Length: " + totalLength); FileInputStream fileInputStream = new FileInputStream(new File( pathToOurFile)); URL url = new URL(urlServer); connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.setDoOutput(true); connection.setUseCaches(false); connection.setRequestMethod("POST"); connection.setRequestProperty("Connection", "Keep-Alive"); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;"); connection.setRequestProperty("Content-Length", "" + totalLength); // number of bytes outputStream = new DataOutputStream( connection.getOutputStream()); //Write out the parameters to the data output stream outputStream.writeBytes(params); bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); buffer = new byte[bufferSize]; bytesRead = fileInputStream.read(buffer, 0, bufferSize); Integer totalSent = paramLength; Integer enLen = 0; //Convert the file to Base64 and Stream the result to the //Data output stream while (bytesRead > 0) { String convetedBase64 = Base64Coder.encodeLines(buffer); convetedBase64 = convetedBase64.replace("=", ""); if (totalSent >= (totalLength - 616)) { Log.i(TAG, "about to send last chunk of data"); convetedBase64 = convetedBase64.substring(0, convetedBase64.length() - 1); } Log.i(TAG, "next data chunk to send: " + convetedBase64.getBytes().length); Log.i(TAG, "'" + convetedBase64 + "'"); enLen = enLen + convetedBase64.length(); outputStream.writeBytes(convetedBase64); totalSent = totalSent + convetedBase64.getBytes().length; Log.i(TAG, "total sent " + totalSent); Log.i(TAG, "actual size: " + outputStream.size()); bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); buffer = new byte[bufferSize]; bytesRead = fileInputStream.read(buffer, 0, bufferSize); // read // into // the // buffer } Log.i(TAG, "enLen: " + enLen); Log.i(TAG, "paddingRequired: " + paddingRequired); for (int x = 0; x < paddingRequired; x++) { outputStream.writeBytes("="); } InputStream is2 = connection.getInputStream(); String output = IOUtils.toString(is2); Log.i(TAG, "Got server response: " + output); fileInputStream.close(); outputStream.flush(); outputStream.close(); } catch (Exception ex) { Log.e(TAG, "caught an exception:" + ex.getMessage()); } 

Sería muy agradecido si alguien pudiera señalar cualquier error en mi código que podría estar causando esto, o sugerir una mejor manera de convertir y subir el archivo.

Soooo … Me las arreglé para encontrar algunas soluciones a este problema, sólo en caso de que alguien tropieza con esta pregunta voy a dejar aquí:

El primero fue escribir los datos en un archivo temporal, así que podría obtener el tamaño después de la conversión en bytes – parecía una buena idea al principio, sin embargo era ineficiente y después de descubrir otros métodos parecía tonto.

El otro método es no especificar una longitud de contenido (yo no sabía que podía hacer esto!). Lamentablemente Android todavía intentó asignar suficiente memoria para la carga, lo que causó problemas.

Si especifica la conexión para usar ChunkedStreamingMode Android, entonces juega agradable y almacena la carga, ahorrando en RAM utilizada (aparte de un extraño error en 2.2).

El código para esto es como tal:

 httppost.setDoInput(true); httppost.setDoOutput(true); httppost.setRequestMethod("POST"); httppost.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); httppost.setChunkedStreamingMode(0); //This specifies the size of the chunks - 0 uses the system default DataOutputStream dos = new DataOutputStream(httppost.getOutputStream()); dos.writeBytes(content); //This code write out the rest of the post body first, for example username or password try { BufferedInputStream dis = new BufferedInputStream(new FileInputStream("path/to/some/file"), 2048); byte[] in = new byte[512]; while (dis.read(in)) > 0) { dos.write(Base64.encode(in, Base64.URL_SAFE)); //This writes out the Base64 data //I used the web safe base64 to save URL encoding it again //However your server must support this dos.write(newLine); //this write out a newline character } dos.flush(); dis.close(); dos.close(); 

¡¡¡Espero que esto ayude!!!

  • Datos de formulario multipart de Android POST
  • Error de autenticación al utilizar HttpPost con DefaultHttpClient en Android
  • Android, enviar y recibir XML a través del método HTTP POST
  • Mejor método para subir imágenes al servidor php en Android?
  • Configurar la autenticación de http
  • El nombre de host no puede ser nulo en HttpResponse para ejecutar android
  • Cómo publicar datos utilizando el método POST en Android
  • ¿Cómo tomar una foto y enviar a HTTP POST solicitud con Android?
  • ¿Cómo subir el archivo usando la biblioteca Volley en android?
  • Android Obtener archivo pdf como resultado de httppost
  • Volea solicitud POST, 400 error
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.