【问题标题】:Java BufferedImage to PNG format Base64 StringJava BufferedImage 转 PNG 格式 Base64 字符串
【发布时间】:2011-11-02 23:36:27
【问题描述】:

我正在尝试将屏幕截图输出为 base64 编码的字符串,但并没有走得太远。到目前为止,我的代码使用 Base64 库(http://iharder.sourceforge.net/current/java/base64/):

    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");

每次我运行这个,“结果”总是一个空字符串,但我不明白为什么。有什么想法吗?

注意:我不想将 png 写入磁盘上的文件。

【问题讨论】:

    标签: java png base64 bufferedimage


    【解决方案1】:

    我关注了xehpuk's 的回答,但在某些浏览器中通过数据 url 渲染时,某些图像的最后几行像素丢失(Chrome 和 Firefox,Safari 似乎可以很好地渲染它们)。我怀疑这是因为浏览器正在尽最大努力解释数据,但最后几个字节的数据丢失了,所以它显示了它可以做什么。

    输出流的包装似乎是导致此问题的原因。 Base64.wrap(OutputStream os) 的文档说明:

    使用后建议及时关闭返回的输出流,在此期间它会将所有可能的剩余字节刷新到底层输出流中。

    因此,根据数据的长度,最后几个字节可能不会从流中刷新,因为没有调用 close()。我对此的解决方案是不用费心包装流,直接对流进行编码:

    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);
      }
    }
    

    这解决了在浏览器中呈现时缺少像素行的问题。

    【讨论】:

    • 您有示例图片吗? ImageIO.write() 在底层ImageOutputStream 上调用close(),这应该关闭Base64.EncOutputStream,这应该将所有剩余字节写入包装的OutputStream。我想知道我错在哪里了。
    • @xehpuk 我想你可能弄错了,ImageIO.write() 明确指出它不会在OutputStream 上调用close()此方法不会关闭提供的@ 987654334@写操作完成后;如果需要,调用者有责任关闭流。
    • 我知道它不会关闭流。它关闭它在内部创建的流。
    • @xehpuk 是的 - 这就是 Javadoc 在这种情况下突出显示非标准行为的原因 - 关闭内部流不会关闭提供的 OutputStream
    • 我们的一位客户向我投诉。他们正在使用libpng.org/pub/png/apps/pngcheck.html。包装它会导致它在浏览器中呈现时失败。当我使用罗伯茨解决方案时。即使在浏览器中渲染,丢失的几个字节也会被渲染并通过测试。
    【解决方案2】:

    以下语句的工作方向错误:

    out.writeTo(b64);
    

    它用out的空字节数组覆盖Base 64数据。

    out 的目的到底是什么?我认为你不需要它。

    更新:

    并且您将图像直接写入os,而不是通过 Base 64 编码器写入。

    以下代码应该可以工作:

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

    【讨论】:

    • 我的 javac 抛出错误。它说找不到符号,然后指向Base64OutputStream(os) 之间的.。我正在使用 jdk1.7.0_51 和 commons-codec-1.4.jar。
    • 我删除了句号,现在是new Base64OutputStream(os),导入的是org.apache.commons.codec.binary.Base64OutputStream。它有效
    • 嗨!我使用完全相同的代码,但我总是在 new Base64OutputStream(os) 构造函数中得到 java.lang.VerifyError。 osjava.io.ByteArrayOutputStreamBase64OutputStream 是来自 commons-codec-1.10.jar 的 org.apache.commons.codec.binary.Base64OutputStream。我错过了什么吗?
    • 听起来好像您的代码是针对一个版本的 Base64OutputStream 构建并针对另一个版本运行的。您的类路径中是否有多个版本的 commons-codec-x.xx.jar?一个是直接的,另一个是通过其他第三方库? (您可以考虑在这里提出一个单独的问题。)
    【解决方案3】:

    使用 Java 8 对图像进行 Base64 编码和解码:

    public static String imgToBase64String(final RenderedImage img, final String formatName) {
        final ByteArrayOutputStream os = new ByteArrayOutputStream();
        try (final OutputStream b64os = Base64.getEncoder().wrap(os)) {
            ImageIO.write(img, formatName, b64os);
        } catch (final IOException ioe) {
            throw new UncheckedIOException(ioe);
        }
        return os.toString();
    }
    
    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);
        }
    }
    

    在您的屏幕截图场景中像这样使用它:

    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");
    

    【讨论】:

      【解决方案4】:

      这对我有用:

      将图像编码为 Base64 字符串

      public static String encodeToString(BufferedImage image, String type) {
          String imageString = null;
          ByteArrayOutputStream bos = new ByteArrayOutputStream();
      
          try {
              ImageIO.write(image, type, bos);
              byte[] imageBytes = bos.toByteArray();
      
              Base64.Encoder encoder = Base64.getEncoder();
              imageString = encoder.encodeToString(imageBytes);
      
              bos.close();
          } catch (IOException e) {
              e.printStackTrace();
          }
          return imageString;
      }
      

      将 Base64 字符串解码为图像

      public static BufferedImage decodeToImage(String imageString) {
          BufferedImage image = null;
          byte[] imageByte;
          try {
              Base64.Decoder decoder = Base64.getDecoder();
              imageByte = decoder.decode(imageString);
              ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
              image = ImageIO.read(bis);
              bis.close();
          } catch (Exception e) {
              e.printStackTrace();
          }
          return image;
      }
      

      【讨论】:

        【解决方案5】:

        实际上,两种不同解决方案的组合对我有用。我的用例是从 zip 文件中读取图像。这是对我有用的代码:

        BufferedImage bgTileSprite = ImageIO.read(inputStream);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(bgTileSprite, "png", os);
        String result = Base64.getEncoder().encodeToString(os.toByteArray());
        LOGGER.info(result);
        

        我通过将结果转换为实际图像来确认结果。效果很好。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-09-28
          • 2021-12-12
          • 2015-11-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多