不同的Base64 JS和Java中相同图像的字符串

时间:2017-11-17 12:16:38

标签: java file tomcat servlets base64

我正在研究在Apache Tomcat上运行的webapp。此webapp从相机创建一个图像,并将base64编码发送到Servlet。然后servlet应该保存该图像。一切都很好,除了当我打开它时图像显示不完整的事实。我已经比较了base64字符串并注意到,发送的字符串和Java打印的字符串之间存在差异。你知道这些差异可能来自哪里吗?

差异如下: - Java String更长 - 第一个~7610个字节相等 - 在第7610个字节之后,字符串不同

private Path saveAsFile(InputStream stream) throws IOException {
    byte[] bytes = new byte[1024];
    String base64String = "";
    while (stream.read(bytes) != -1) {
        String tmp = new String(bytes, StandardCharsets.UTF_8);
        base64String += tmp;
    }
    //this prints out a longer base64 String than the Javascript part
    System.out.println(base64String);
    String replaced = base64String.replaceFirst("data:image/png;base64,", "");

    byte[] replacedbytes = Base64.decodeBase64(replaced);

    Path temp = Files.createTempFile("photo", ".png");
    FileOutputStream fos = new FileOutputStream(temp.toFile());
    fos.write(replacedbytes);
    return temp;
}

提前致谢!

1 个答案:

答案 0 :(得分:2)

这:

byte[] bytes = new byte[1024];
String base64String = "";
while (stream.read(bytes) != -1) {
    String tmp = new String(bytes, StandardCharsets.UTF_8);
    base64String += tmp;
}

不是在java中读取流的有效方法。 (基本上,您必须将stream.read()返回的值视为缓冲区中实际有效的字节数,并使用new String(bytes, 0, theAboveValue, charset)作为字符串构造函数。

除此之外,您没有正确关闭FileOutputStream

但即便如此,这里还有其他问题(在连接流之前解码字符集可能是一个等待在这里发生的错误 - 但这将起作用,因为base64实际上是纯ASCII,没有多字节字符会在这里引起错误。纯粹的运气)。

清理代码的IO部分,应该看起来像:

private Path saveAsFile(InputStream stream) throws IOException {
byte[] bytes = new byte[1024];
int readBytes = 0;
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
while ((readBytes = stream.read(bytes)) != -1) {
    buffer.write(bytes, 0, readBytes);
}

//this prints out a longer base64 String than the Javascript part
String base64String = new String(buffer.getBytes, "US-ASCII");
String replaced = base64String.replaceFirst("data:image/png;base64,", "");
byte[] replacedbytes = Base64.decodeBase64(replaced);

Path temp = Files.createTempFile("photo", ".png");
FileOutputStream fos = new FileOutputStream(temp.toFile());
fos.write(replacedbytes);
fos.close();// You should not miss that too! And put it in a "finally" step not to leak file descriptors.
return temp;

}

这可能效果更好,但就内存消耗而言(将字节转换为字符串,将字节转换为字节,每一步都复制一次!)。

有更好的方法! 您可能更好地使用库来进行此类复制。 Apache Commons IO和Commons Codec有很好的Base64InputStreamIOUtils.copy类,可以在这里提供帮助。

在这种情况下,它可以是:

private Path saveAsFile(InputStream stream) throws IOException {
    // Maybe you should advance the stream to skip the "data/image stuff"...
   //stream.skip(theActualNumberOfBytesToSkip);
    try (Base64InputStream decoded = new Base64InputStream(stream); FileOutputStream file = /*whatever*/) {
        IOUtils.copy(decoded, file);
    }
}