2011-08-24 29 views
12

Estoy tratando de obtener una salida de captura de pantalla como una cadena codificada en base64 pero sin llegar muy lejos. El código que tengo hasta ahora utiliza una biblioteca de base 64 (http://iharder.sourceforge.net/current/java/base64/):Java BufferedImage to PNG format Base64 String

Robot robot = new Robot(); 
    Rectangle r = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); 
    BufferedImage bi = robot.createScreenCapture(r); 
    ByteArrayOutputStream os = new ByteArrayOutputStream(); 
    OutputStream b64 = new Base64.OutputStream(os); 
    ImageIO.write(bi, "png", os); 
    ByteArrayOutputStream out = new ByteArrayOutputStream(); 
    out.writeTo(b64); 
    String result = out.toString("UTF-8"); 

Cada vez que funciono esto, "número" es siempre una cadena vacía, pero no entiendo por qué. ¿Algunas ideas?

Nota: No quiero tener que escribir el png en un archivo en el disco.

Respuesta

5

Seguí la respuesta xehpuk's pero tuve problemas con ciertas imágenes que tenían las últimas filas o faltaban píxeles cuando se procesaban en ciertos navegadores a través de una url de datos (Chrome y Firefox, Safari parecía hacerlos bien). Sospecho que esto se debe a que el navegador está haciendo lo mejor para interpretar los datos, pero faltaron los últimos bytes de datos, por lo que muestra lo que puede.

El enrollamiento del flujo de salida parece ser la causa de este problema. La documentación para Base64.wrap(OutputStream os) explica:

Se recomienda cerrar rápidamente el flujo de salida de regresar después de su uso, durante el cual se eliminará todos los posibles restos de bytes al flujo de salida subyacente.

Así que dependiendo de la longitud de los datos, es posible que los últimos bytes no se vacían de la corriente porque no close() se llama en ella. Mi solución a esto fue para no molestar envolver la corriente y simplemente codificar el flujo directamente:

public static String imgToBase64String(final RenderedImage img, final String formatName) 
{ 
    final ByteArrayOutputStream os = new ByteArrayOutputStream(); 

    try 
    { 
    ImageIO.write(img, formatName, os); 
    return Base64.getEncoder().encodeToString(os.toByteArray()); 
    } 
    catch (final IOException ioe) 
    { 
    throw new UncheckedIOException(ioe); 
    } 
} 

Esto resuelve los problemas con las filas de píxeles que faltan cuando se representa en un navegador.

+0

¿Tiene una imagen de ejemplo? 'ImageIO.write()' llama a 'close()' en el 'ImageOutputStream' subyacente que debe cerrar el' Base64.EncOutputStream' que debe escribir todos los bytes restantes en el 'OutputStream' envuelto. Me gustaría saber dónde me equivoco. – xehpuk

+0

@xehpuk Creo que puede estar equivocado, 'ImageIO.write()' establece explícitamente que no llama 'close()' en 'OutputStream': *** Este método no cierra el' OutputStream' proporcionado después de la la operación de escritura se ha completado; es la responsabilidad de la persona que llama para cerrar la secuencia, si lo desea. *** –

+0

Sé que no cierra la secuencia. Cierra la transmisión que crea internamente. – xehpuk

15

la siguiente declaración funciona en la dirección equivocada:

out.writeTo(b64); 

sobrescribe la base 64 de datos con la matriz de bytes vacía de out.

¿Cuál es el propósito de out de todos modos? No creo que lo necesites

Actualización:

Y se escribe la imagen directamente a os en vez de escribir a través del codificador de base 64.

El siguiente código debería funcionar:

... 
ByteArrayOutputStream os = new ByteArrayOutputStream(); 
OutputStream b64 = new Base64.OutputStream(os); 
ImageIO.write(bi, "png", b64); 
String result = os.toString("UTF-8"); 
+0

Mi javac está arrojando un error. Dice que no puede encontrar el símbolo, luego apunta al '.' entre' Base64' y 'OutputStream (os)'. Estoy usando jdk1.7.0_51 y commons-codec-1.4.jar. – Tgwizman

+1

Quité el período y ahora es 'nuevo Base64OutputStream (os)' y la importación es 'org.apache.commons.codec.binary.Base64OutputStream'. Funciona – Tgwizman

+0

¡Hola!Estoy usando exactamente el mismo código, pero siempre obtengo java.lang.VerifyError en el 'nuevo constructor Base64OutputStream (os)'. 'os' es' java.io.ByteArrayOutputStream'. 'Base64OutputStream' es' org.apache.commons.codec.binary.Base64OutputStream' de commons-codec-1.10.jar. ¿Me estoy perdiendo de algo? – Chechulin

9

codificación Base64 y decodificación de imágenes utilizando Java 8:

public static String imgToBase64String(final RenderedImage img, final String formatName) { 
    final ByteArrayOutputStream os = new ByteArrayOutputStream(); 
    try { 
     ImageIO.write(img, formatName, Base64.getEncoder().wrap(os)); 
     return os.toString(StandardCharsets.ISO_8859_1.name()); 
    } catch (final IOException ioe) { 
     throw new UncheckedIOException(ioe); 
    } 
} 

public static BufferedImage base64StringToImg(final String base64String) { 
    try { 
     return ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(base64String))); 
    } catch (final IOException ioe) { 
     throw new UncheckedIOException(ioe); 
    } 
} 

utilizar de esta manera para su pantalla escenario:

final Robot robot = new Robot(); 
final Rectangle r = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); 
final BufferedImage bi = robot.createScreenCapture(r); 
final String base64String = imgToBase64String(bi, "png");