base 64 encoding/decoding 后图像文件大小不同

image file size is different after base 64 encoding/decoding

我正在使用以下测试读取文件,对其进行 base 64 编码,然后将 base 64 解码回新图像。我注意到新的图像文件大小(转换后)明显小于原始图像,这让我觉得不知何故,部分图像数据在转换过程中丢失了。我可以看到图像,但担心图像质量。任何关于我可能做错了什么的见解将不胜感激。

测试class:

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 encoder/decoder 以及 sun.java.misc 中的(我意识到我不需要使用)。关于可能导致图像尺寸缩小的任何想法(如果需要,我更愿意自己做,使用 imagemagick 或 graphicsmagick 等)。

原始图像为 1.2 MB(1,249,934 字节),但新图像为 354.5 kB(354,541 字节)- width/height 两幅图像相同。

正如@JBNizet 在他的评论中指出的那样,大小变化的原因(大小也可能增加,具体取决于输入图像和压缩设置),是因为您不只是 encoding/decoding二进制数据 to/from Base64,您还使用 JPEG 编码(使用默认编码设置)重新编码 图像数据(两次)。除非原始图像使用完全相同的设置进行编码,否则您将失去一些精度,并且文件大小将发生变化。

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

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

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