基本64编码/解码后的图像文件大小不同

时间:2016-04-17 15:13:38

标签: java base64 javax.imageio

我使用以下测试来读取文件,base 64对其进行编码,然后将base 64解码回新图像。我注意到新的图像文件大小(转换后)明显小于原始图像,导致我认为不知何故,部分图像数据在转换中丢失。我可以看到图像,但我担心图像质量。任何关于我可能做错的见解都将非常感激。

测试类:

package test;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

public class ImageTest {  //should be B64Test

    public static void main(String[] args) {
        ImageTest imageTest = new ImageTest();
        try {
            BufferedImage img = null;
            BufferedImage finalImg = null;
            try {
              //  img = ImageIO.read(new File("/home/user/Desktop/test1.jpg")); 
                img = ImageIO.read(Files.newInputStream(Paths.get("/home/user/Desktop/test1.jpg")));
                //encode base64 and print
                final String base64encoded = ImageConverter.encodeToString(img, "jpeg");
                System.out.println("read file " + base64encoded);

                //convert base64 string to image
                finalImg = ImageConverter.decodeToImage(b64encoded);
                ImageIO.write(finalImg, "jpeg", new File("/home/user/Desktop/test2.jpg"));
            } catch (IOException e) {
                System.out.println("exception " + e);
            }

        } catch (Exception e) {
            System.out.println("exception " + e);
        }

    }

}

ImageConverter

package test;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;

public class ImageConverter {

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


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

            BASE64Encoder encoder = new BASE64Encoder();
            imageString = encoder.encode(imageBytes);

            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return imageString;
    }


    public static BufferedImage decodeToImage(String imageString) {

        BufferedImage image = null;
        byte[] imageByte;
        try {
            BASE64Decoder decoder = new BASE64Decoder();
            imageByte = decoder.decodeBuffer(imageString);
            ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
            image = ImageIO.read(bis);
            bis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return image;
    }
}

我可以尝试测试jdk8中可用的base 64编码器/解码器以及sun.java.misc中的那个(我意识到我不需要使用它)。关于什么可能导致图像大小缩小的任何想法(如果需要使用imagemagick或graphicsmagick等,我宁愿自己这样做。)

原始图像为1.2 MB(1,249,934字节),但新图像为354.5 kB(354,541字节) - 两个图像的宽度/高度相同。

1 个答案:

答案 0 :(得分:1)

正如@JBNizet在他的评论中指出的那样,尺寸变化的原因(尺寸可能会增长,取决于输入图像和压缩设置),就是你不只是将二进制数据编码/解码为/从Base64开始,您还使用JPEG编码(默认编码设置)重新编码图像数据(两次)。除非使用完全相同的设置对原始图像进行编码,否则会丢失一些精度,文件大小也会改变。

文件大小减少的另一个可能原因是BufferedImage不携带原始JPEG文件中包含的任何元数据。因此,重新编码JPEG的过程也会丢失任何Exif或XMP元数据,缩略图,颜色配置文件等。根据图像的来源,这可能会占据文件大小的很大一部分。

再次,正如@JBNizet所说,在这种情况下最好不要涉及ImageIO,只需使用普通文件I / O并使用Base64对原始字节进行编码,然后再次解码以恢复原始文件文件内容完全正确。

PS:如果你打算在Base64编码/解码之间对图像进行图像处理,你当然需要解码图像数据(使用ImageIO或类似),但你应该尝试做它只有一次(为了更好的性能),并且可能会考虑保留元数据。此外,我认为图像编码/解码和Base64编码/解码是分开的问题,不应该像现在一样交错。将其拆分为better separation of concerns