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.
- Error en httppost con Android
- HttpPost en Android - personajes no reconocidos
- Android: Http post con parámetros que no funcionan
- Cómo enviar la solicitud a la solicitud HTTP POST al servidor
- Cómo hacer llamada httpPost con cuerpo codificado json?
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.
- ¿Cómo imprimo el contenido de la solicitud httprequest?
- Android Multipart POST para Google App Engine no funciona
- Json no funciona con HttpPost probablemente alrededor de setEntity
- Android cómo enviar int y doble como namevaluepair como http post
- Solicitud de volea de Android usando pares de valor de nombre
- Android: Subir imagen al servidor PHP
- Problemas de memoria al publicar archivos desde android
- Enviar datos a la página cargada en WebView
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!!!
- Android Eclipse ¿Cómo puedo guardar renderizado GLsurfaceView como * .png image
- Recuperar el contexto de llamada en un BroadcastReceiver ()