通过 RestTemplate 将文件作为 byte[] 从 Java 服务发送到 Java 服务时是否需要额外的 base64 编码?
Is additional base64 encoding necessary when sending files as byte[] from Java service to Java Service via RestTemplate?
我在 post 请求中通过 json 正文从客户端 (Java) 向服务器 (Java) 使用 Spring RestTemplate 和 RestController.
数据在客户端以POJO的形式存在,在服务器端会被解析成相同结构的POJO。
在客户端上 我正在将 Files.readAllBytes 的文件转换为 byte[] 并将其存储在内容字段中。
在服务器端,包括 byte[] 在内的整个对象将使用 JAXB 注释编组为 XML。
class BinaryObject {
String fileName;
String mimeCode;
byte[] content;
}
一切正常,运行符合预期。
我听说在将日期传输到服务器之前对内容字段进行编码并在将其编组到 XML.
之前对其进行解码可能是有益的
我的问题
是否有必要或建议使用 base64 额外编码/解码内容字段?
TL;DR
据我所知,您当前的实施并没有违背任何良好做法。有人可能会质疑设计(在 JSON 中交换文件?在 XML 中存储二进制文件?),但这是一个单独的问题。
仍然有可能优化的空间,但是您使用的工具集(例如Spring rest template + Spring Controler + JSON serialization (jackson) + XML 使用 JAXB) 对您隐藏了可能的优化。
你必须仔细权衡利弊围绕你舒适的“automat(g)ical”序列化工作,这些序列化在今天运行良好,看看它是否值得麻烦调整一下。
我们仍然可以讨论可以做什么的理论。
关于 Base64 的讨论
Base64 编码以一种有效的方式对纯文本格式的二进制数据进行编码(例如 MIME 结构,例如电子邮件或某些 HTTP 正文,JSON、XML、...)但它具有两个成本:第一个是不可忽略的尺寸增加(~33% 尺寸),第二个是 CPU 时间。
有时,(但你必须分析,检查你的情况是否如此),这个成本是不可忽略的,尤其是。对于大文件(由于框架中的一些缓冲和 char/byte 转换,您很容易最终使用 Java 堆中编码文件大小的 4 倍)。
当以 10 requests/sec 处理 10kb 文件时,这通常不是问题。
但是 100MB 的文件 req/second,那是另一个球场。
所以你必须检查(我怀疑你的典型服务器将达到 100 req/s 10MB 的文件,因为这是 1GB/s 的传入网络带宽)。
您当前流程中可优化的内容
在您当前的流程中,您进行了多次编码:客户端需要对从文件中读取的字节进行 Base64 编码。
当请求到达服务器时,服务器将 base64 解码为 byte[]
,然后您的 XML 序列化 (JAXB) 将 byte[]
重新转换为 base64。
所以实际上,“你”(更确切地说,事物的 REST 控制器端)解码了 base64 内容,这一切都是徒劳的,因为事物的 XML 端可以直接使用它。
可以做什么
几件事。
调用站点需要 base64 吗?
首先,您不必在客户端进行编码。使用 JSON 时别无选择,但世界并没有等待 JSON 通过 HTTP 交换文件(例如任意二进制内容)。
如果您的内容是一个文件名、一个 MIME 类型和一个文件 body,那么完全没有 JSON 的标准直接 HTTP 调用就完全没问题。
MIME 类型可以映射到 Content-Type
HTTP Header,Content-Disposition
HTTP header 中的文件名,以及原始 HTTP 的内容 body。不需要 base64(但是你需要你的 server-side 来接受原始的 HTTP 内容)。这是标准的。
此更改将允许您删除编码(客户端),降低调用的网络大小(减少约 33%),并在服务器端删除一个解码。服务器只需对原始流进行 base64 编码(一次)以生成 XML,您甚至不需要为此缓冲整个文件内容(您必须稍微调整一下 JAXB 模型,但是您可以 JAXB 直接序列化来自 InputStream
的字节,这意味着几乎没有缓冲区,并且由于您的 CPU 编码速度可能比您的网络服务内容更快,因此不会产生真正的延迟。
如果出于某种原因,这不是一个选项,假设您的客户必须发送 JSON(因此是 base64 内容)
能不能避免服务器端解码
有点。您可以使用 server-side bean,其中 content
实际上是 String
而不是 byte[]
。这是 hacky,但是您的 REST 控制器将不再反序列化 base64,它将保持“原样”,这是一个 JSON 字符串(恰好是 base64 编码的内容,但控制器不关心)。
因此您的服务器将节省一次 base64 解码的 CPU 成本,但作为交换,您将在 java 堆中拥有一个 base64 字符串(与原始 byte[]
, 在 Java >=9 上增加 33% 大小,在 Java < 9 上增加 166% 大小。
如果你想从中获利,你还必须调整你的 JAXB 以将 base64 编码的 String
视为 byte[]
,据我所知这不是微不足道的,除非您以接受 String
而不是 byte[]
的方式修改 JAXB object(如果您的 JAXB object 是从 XML 架构,这可能真的很难实现)
总而言之,这要困难得多 - 如果您在 这个 特定问题上没有真正碰壁,那么可能太难了。
一些其他的东西
您的文件是纯二进制文件,还是实际上是文本文件?如果有文字,您可能会受益于使用CDATA
在 XML 端编码而不是 base64 ?
您的 XML 实际上是 SOAP 调用吗?如果是这样,并且如果服务支持 MTOM,您可以完全避免使用 base64,但这是一个完全不同的主题。
我在 post 请求中通过 json 正文从客户端 (Java) 向服务器 (Java) 使用 Spring RestTemplate 和 RestController.
数据在客户端以POJO的形式存在,在服务器端会被解析成相同结构的POJO。
在客户端上 我正在将 Files.readAllBytes 的文件转换为 byte[] 并将其存储在内容字段中。
在服务器端,包括 byte[] 在内的整个对象将使用 JAXB 注释编组为 XML。
class BinaryObject {
String fileName;
String mimeCode;
byte[] content;
}
一切正常,运行符合预期。 我听说在将日期传输到服务器之前对内容字段进行编码并在将其编组到 XML.
之前对其进行解码可能是有益的我的问题
是否有必要或建议使用 base64 额外编码/解码内容字段?
TL;DR
据我所知,您当前的实施并没有违背任何良好做法。有人可能会质疑设计(在 JSON 中交换文件?在 XML 中存储二进制文件?),但这是一个单独的问题。
仍然有可能优化的空间,但是您使用的工具集(例如Spring rest template + Spring Controler + JSON serialization (jackson) + XML 使用 JAXB) 对您隐藏了可能的优化。
你必须仔细权衡利弊围绕你舒适的“automat(g)ical”序列化工作,这些序列化在今天运行良好,看看它是否值得麻烦调整一下。
我们仍然可以讨论可以做什么的理论。
关于 Base64 的讨论
Base64 编码以一种有效的方式对纯文本格式的二进制数据进行编码(例如 MIME 结构,例如电子邮件或某些 HTTP 正文,JSON、XML、...)但它具有两个成本:第一个是不可忽略的尺寸增加(~33% 尺寸),第二个是 CPU 时间。
有时,(但你必须分析,检查你的情况是否如此),这个成本是不可忽略的,尤其是。对于大文件(由于框架中的一些缓冲和 char/byte 转换,您很容易最终使用 Java 堆中编码文件大小的 4 倍)。
当以 10 requests/sec 处理 10kb 文件时,这通常不是问题。 但是 100MB 的文件 req/second,那是另一个球场。
所以你必须检查(我怀疑你的典型服务器将达到 100 req/s 10MB 的文件,因为这是 1GB/s 的传入网络带宽)。
您当前流程中可优化的内容
在您当前的流程中,您进行了多次编码:客户端需要对从文件中读取的字节进行 Base64 编码。
当请求到达服务器时,服务器将 base64 解码为 byte[]
,然后您的 XML 序列化 (JAXB) 将 byte[]
重新转换为 base64。
所以实际上,“你”(更确切地说,事物的 REST 控制器端)解码了 base64 内容,这一切都是徒劳的,因为事物的 XML 端可以直接使用它。
可以做什么
几件事。
调用站点需要 base64 吗?
首先,您不必在客户端进行编码。使用 JSON 时别无选择,但世界并没有等待 JSON 通过 HTTP 交换文件(例如任意二进制内容)。
如果您的内容是一个文件名、一个 MIME 类型和一个文件 body,那么完全没有 JSON 的标准直接 HTTP 调用就完全没问题。
MIME 类型可以映射到 Content-Type
HTTP Header,Content-Disposition
HTTP header 中的文件名,以及原始 HTTP 的内容 body。不需要 base64(但是你需要你的 server-side 来接受原始的 HTTP 内容)。这是标准的。
此更改将允许您删除编码(客户端),降低调用的网络大小(减少约 33%),并在服务器端删除一个解码。服务器只需对原始流进行 base64 编码(一次)以生成 XML,您甚至不需要为此缓冲整个文件内容(您必须稍微调整一下 JAXB 模型,但是您可以 JAXB 直接序列化来自 InputStream
的字节,这意味着几乎没有缓冲区,并且由于您的 CPU 编码速度可能比您的网络服务内容更快,因此不会产生真正的延迟。
如果出于某种原因,这不是一个选项,假设您的客户必须发送 JSON(因此是 base64 内容)
能不能避免服务器端解码
有点。您可以使用 server-side bean,其中 content
实际上是 String
而不是 byte[]
。这是 hacky,但是您的 REST 控制器将不再反序列化 base64,它将保持“原样”,这是一个 JSON 字符串(恰好是 base64 编码的内容,但控制器不关心)。
因此您的服务器将节省一次 base64 解码的 CPU 成本,但作为交换,您将在 java 堆中拥有一个 base64 字符串(与原始 byte[]
, 在 Java >=9 上增加 33% 大小,在 Java < 9 上增加 166% 大小。
如果你想从中获利,你还必须调整你的 JAXB 以将 base64 编码的 String
视为 byte[]
,据我所知这不是微不足道的,除非您以接受 String
而不是 byte[]
的方式修改 JAXB object(如果您的 JAXB object 是从 XML 架构,这可能真的很难实现)
总而言之,这要困难得多 - 如果您在 这个 特定问题上没有真正碰壁,那么可能太难了。
一些其他的东西
您的文件是纯二进制文件,还是实际上是文本文件?如果有文字,您可能会受益于使用CDATA
在 XML 端编码而不是 base64 ?
您的 XML 实际上是 SOAP 调用吗?如果是这样,并且如果服务支持 MTOM,您可以完全避免使用 base64,但这是一个完全不同的主题。