Red de conocimiento del abogados - Respuesta jurídica de la empresa - La carga de Android a BASE64 solicita java.lang.OutOfMemoryError

La carga de Android a BASE64 solicita java.lang.OutOfMemoryError

También me he encontrado con este problema recientemente, pero no he encontrado ninguna solución directa relevante y efectiva en línea.

Más tarde vi un artículo que explicaba el principio de codificación base64 y lo resolví después de investigar un poco.

Generalmente, cuando se encuentra este problema, se trata del problema de "carga de archivos grandes". Además de la posibilidad de OOM durante la codificación base64 durante el proceso de "carga de archivos grandes", en realidad existen otros problemas. aunque no se mencionaron en la pregunta, tal vez porque este problema aún no se ha resuelto, por lo que todavía no he encontrado otros problemas, por lo que resolveré este problema en torno a la "carga de archivos grandes".

(Podéis ver claramente el turno de preguntas a continuación)

——————————————————————

Hacer Durante el proyecto, encontré un requisito:

En el cliente Java, use blogs.com/luguo3000/p/3940197.html" target="_blank"gt; blogs.com/luguo3000 /p/3940197 .htmllt;/agt;

Supongamos que hay un archivo A (archivo txt, que contiene contenido de texto "ABCDEFG"), convertido a InputStream-gt byte[]

Sus códigos ASIIC corresponden a 65, 66, 67, 68, 69, 70, 71 respectivamente

La representación binaria es:

1000001 1000010 1000011 1000100 1000101 1000110 1000111

Para el bit alto después del relleno con ceros:

01000001 01000010 01000011 01000100 01000101 01000111

Rendimiento real en memoria:

0100000101000010010000110100010 0010001 010100011001000111

Y codificación base64, los caracteres utilizados incluyen (A-Z, a-z, 0-9,, /, =) estos caracteres legibles regulares. La razón y el propósito de usar la codificación base64 es convertir algunos caracteres confusos y caracteres ilegibles en caracteres legibles normales;

(Debido a que el protocolo de comunicación subyacente de Java u otros protocolos de comunicación se utiliza en comunicaciones remotas en muchos lugares, no admite la transmisión de algunos caracteres confusos, por lo que es necesario convertir los caracteres confusos en caracteres legibles normales)

Por ejemplo, para el carácter 'kan', el conjunto de codificación de algunos protocolos de transmisión no lo reconoce, por lo que no se puede transmitir directamente y debe transcodificarse en base64. /p>

'kan' El valor de codificación UTF-8 es 30681 y la representación binaria es 111011111011001-gt; (0)111011111011001

Se necesitan dos bytes para almacenar 01110111 11011001

Solo codificación base64 (A-Z, a-z, 0-9, , /, =). Debe enfatizarse que en la especificación de codificación base64, el carácter 'A' no es igual a 65, ni 'B' es 66. .. valor) es el siguiente:

En otras palabras, el carácter normal 'A'=65=01000001; y el carácter base64 'A'=0=00000000;

El El carácter base64 representa El valor binario no puede representar directamente el carácter 'kan', porque el rango de valores del carácter base64 está entre 0 y 63 (el valor binario está entre (00)000000~(00)111111).

¿Cómo representar 01110111 11011001 a través de los valores entre (00)000000~(00)111111?

Este es el algoritmo de codificación de base64

El valor binario de un carácter base64 está entre (00)000000~(00)111111, lo que significa que puede representar entre 000000~111111 A número binario entre, los dígitos efectivos de un carácter base64 son los últimos 6 dígitos.

¿Cómo representar bytes regulares en 8 bits mediante caracteres base64 en 6 bits?

El mínimo común múltiplo de 6 y 8 es 24, es decir, cada 4 caracteres base64 pueden representar 3 bytes normales;

Vuelva al archivo A hace un momento, el proceso de codificación :

(Archivo inicial A)-gt; "ABCDEFG"

(Convertir a código UTF-8 int)-gt; p>("ABCDEFG "representación binaria; 7 bytes) -gt; 1000001 1000010 1000011 1000100 1000101 1000110 1000111

(relleno de ceros de bit alto) -gt; 01000100 01 000101 01000110 01000111

(Escritura continua)-gt; 01000001010000100100001101000100010001010100011001000111

(Divida todos los bits en unidades de 6 bits; obtenga 10 bytes)-gt; 00011 010001 000100 010101 000110 010001 11

(Presione Se vuelve a dividir la relación correspondiente de 6bit*4=8bit*3; se obtienen 3 grupos de 6*4 bytes) -gt (010000 010100 001001 000011) (010001 000100 010101 000110) (010001 11)

(Bit alto Agregue 2 ceros; agregue ceros al bit bajo al final) -gt; >

(Binario conversión de valor Contar como decimal)-gt; (16 20 9 3) (17 4 21 6) (17 48)

(Basado en la tabla de correspondencia entre valores y caracteres codificados en base64, el carácter base64 correspondiente a se obtiene el valor decimal anterior)-gt; (Q U J D) ( R E V G) (R w)

(Se requiere que cada grupo de caracteres base64 sea 4 y la posición en blanco se complete con '=" carácter)-gt; (Q U J D) (R E V G) (R w = =)

(Resultado final de transcodificación del archivo A)-gt; Como demostración aquí, debido a que los archivos de texto son legibles por máquinas y humanos en situaciones reales, muchos cuando el archivo de destino para la transcodificación no es un archivo de texto, no se puede expresar en forma de una cadena legible, pero se expresará. directamente en formato binario

La razón del aumento de tamaño es porque 3 bytes = 24 bits = divídalo en 4 6 bits y rellene los bits altos de los 4 6 bits con ceros para obtener 4 bytes

Es decir, 3 bytes normales generarán 4 bytes base64 después de la codificación base64. Esta es la razón por la cual el tamaño del archivo aumentará en 1/3 después de la transcodificación base64

—————. —————

Se ha explicado el principio de codificación base64. Veamos los puntos ahora. Codificación de segmento

ByteArrayOutputStream os1 = new ByteArrayOutputStream();

InputStream file1 = nuevo FileInputStream(ruta);

byte[] buf1 = nuevo byte[1024];

p>

int cuenta1;

mientras((cuenta1 = archivo1.read(buf1)) != -1)

{

os1.write (Base64.encodeBase64(buf1), 0, count1); // Puede encontrar un problema: después de la codificación Base64.encodeBase64(buf1), el volumen aumentará en 1/3, por lo que aquí está el valor real después de Base64.encodeBase64( buf1) conversión de codificación La longitud no es igual a count1, por lo que la cantidad de caracteres base64 realmente escritos en os1 es solo 3/4 de la cantidad de caracteres producidos por la codificación Base64.encodeBase64 (buf1)

os1. flush();

}

file1.close();

System.out.println(os1.toString());

Después de la modificación:

System.out.println(os1.toString());

Después de la modificación:

p>

ByteArrayOutputStream os1 = new ByteArrayOutputStream( );

InputStream file1 = nuevo FileInputStream(ruta);

byte[] byteBuf = nuevo byte[1024];

byte[] base64ByteBuf;

while(file1.read(byteBuf) != -1)

{

base64ByteBuf = Base64.encodeBase64( byteBuf>

os1); .write(base64ByteBuf, 0, base64ByteBuf.length);

os1.flush()

}

file1.close();

System.out.println(os1.toString());

——————————

Después de la modificación, se descubrió que el resultado de la codificación segmentada ha cambiado y es diferente a la anterior

Pero aún no es el resultado correcto

La razón es que la unidad básica de caracteres base64 es (6 bits *4=4 bytes) , y 3 bytes regulares (8bit*3) pueden producir exactamente 4 bytes base64

La razón fundamental es que si el número de bytes regulares para codificar no es múltiplo de 3, habrá 1 o 2 bytes se deja al final, y el resultado de la codificación de estos 1 o 2 bytes producirá el carácter '=";

Cuando se usa 1024 como búfer de codificación segmentado, el resultado de la codificación es 3 3 3... 1

Es decir, quedará 1 byte cada vez

Cuando no se utiliza la codificación segmentada, cuando se codifica el byte 1024, "El 1 byte restante será continuo con el siguientes bytes, y el carácter '=" no se generará

(Cuando se codifica en base64 un carácter de byte, el carácter '=" nunca se generará en el medio, porque solo pueden quedar 1 o 2 bytes al final, por lo que al codificar un carácter de byte, solo se pueden generar 1 o 2 caracteres de finalización '=" al final)

—— ————————

La solución es utilizar un múltiplo común de 3 como tamaño del búfer

Después de la modificación:

ByteArrayOutputStream os1 = new ByteArrayOutputStream( );

I

nputStream file1 = new FileInputStream(ruta);

byte[] byteBuf = nuevo byte[3*1000];

byte[] base64ByteBuf;

while( file1.read(byteBuf) != -1)

{

base64ByteBuf = Base64.encodeBase64(byteBuf);

os1.write(base64ByteBuf, 0, base64ByteBuf.length);

os1.flush();

}

file1.close();

System.out. println(os1.toString());

Los resultados de la prueba han cambiado nuevamente

Ya no hay un carácter '=" en el medio, porque tiene 3 bytes y 3 palabras. cada vez que se codifica la sección, no quedan bytes adicionales

Después de la comparación, se encontró que el resultado de la sección central es normal

————————— —

Sin embargo, se descubrió que los dos resultados de transcodificación al final son ligeramente diferentes

La razón es que se supone que la longitud del archivo A es de 3001 bytes;

En el segundo ciclo Al leer, solo se lee 1 byte válido y los 2999 bytes restantes de byteBuf son bytes no válidos. Sin embargo, al codificar, los 2999 bytes no válidos adicionales también se codifican.

(If. es una transcodificación no segmentada, esta situación no ocurrirá)

Solución:

ByteArrayOutputStream os1 = new ByteArrayOutputStream();

InputStream file1 = new FileInputStream( ruta);

byte[] byteBuf = new byte[3*1000];

byte[] base64ByteBuf;

int count1; bytes válidos leídos del archivo cada vez

while((count1=file1.read(byteBuf)) != -1)

{

if(count1 !=byteBuf.length) //Si el número de bytes válidos no es 3*1000, significa que el archivo se ha leído hasta el final y no es suficiente para llenar el byteBuf

{

byte[] copy = Arrays.copyOf(byteBuf, count1); //Intercepta el segmento de bytes que contiene el número válido de bytes de byteBuf

base64ByteBuf = Base64.encodeBase64(copy); Codifique el segmento de bytes válido

}

else

{

base64ByteBuf = Base64.encodeBase64(byteBuf);

}

os1.write(base64ByteBuf, 0, base64ByteBuf.length);

os1.flush()

}

archivo1

.close();

System.out.println(os1.toString());

En este punto, la codificación segmentada base64 está completa. El código central para cargar archivos grandes está completo.

De hecho, es muy sencillo cambiar el código, pero si no conoces el motivo y el principio, no puedes hacer algo de la nada.

Para mí Originalmente solo quería responderlo de manera casual, pero no esperaba responderlo. En el proceso, descubrí que había muchos peligros que no había descubierto. En el proceso de respuesta, también mejoré los errores que no entendí y no encontré. Sin mencionar que tienes que llegar al fondo de cada punto de conocimiento que encuentres, pero en el desarrollo real, cada problema práctico que puedas encontrar personalmente es una excelente oportunidad para entrenarte. Esta oportunidad de tocar y resolver problemas a corta distancia es rara. . Aunque hay muchos otros problemas en desarrollo que también son importantes, no puedes comentarlos a menos que los hayas encontrado tú mismo. Por lo tanto, si encuentra un problema durante el desarrollo, se recomienda determinar aproximadamente la causa.

Después de aclarar el principio, incluso si aparecen otras "variantes" de este problema en el futuro, puede encontrar la causa y resolverla usted mismo, pero simplemente pegar y copiar no puede lograrlo.