我正在研究在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;
}
提前致谢!
答案 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有很好的Base64InputStream
和IOUtils.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);
}
}