Byte array versus Base 64 string in RESTful web service - java

My REST web service has to send an image file to the client. I am confused between 2 options : send the image as a byte array, or should I encode it as a base 64 string ? What are the pros and cons of each ? I may have to compress the image using gzip...should it create problem with any one of methods ? I may even need to expose my method as a SOAP service, which method should I prefer in that case ?
Thanks !!

The wonderful thing about a RESTful interface is that it's just HTTP. So if you expose the "byte array" version via REST, any browser can do an HTTP GET on your REST URL and receive and directly render your image. Returning the payload verbatim is far more RESTful than placing an encoding on it. There is not much to recommend an extra base64 encoding layer via REST.
If you're returning SOAP, you absolutely want to return a base64 string. Raw binary data is not compatible with XML, upon which SOAP is built. You can try to work around it via MTOM, but for general-purpose compatibility with SOAP clients you probably want inlined base64-encoded data.
In general, there's no benefit to be gained by compressing an image file. The image formats themselves internally involve compression, and a second compression pass will not gain any more space savings.

If your service returnes JSON or XML (image + some information), than you should encode image in base 64 because both of them string based, and you want to transfer byte array. The only question, whether you should make it yourself or it should be made my framework you use.
Situation with GZip is clear - relay compression to the servlet container (like tomcat - you can configure, whether response should be gzipped). Alternatively you can use something like GZipFilter.

Related

Is additional base64 encoding necessary when sending files as byte[] from Java service to Java Service via RestTemplate?

I am sending data via json body in a post request from a client (Java) to a server (Java) using a Spring RestTemplate and RestController.
The data is present as a POJO on the client and will be parsed into a POJO with the same structure on the server.
On the client I am converting a file with Files.readAllBytes to byte[] and store it in the content field.
On the server side the whole object including the byte[] will be marshalled to XML using JAXB annotations.
class BinaryObject {
String fileName;
String mimeCode;
byte[] content;
}
Everything is working fine and running as intended.
I heard it could be beneficial to encode the content field before transmitting the date to the server and decode it there before it is marshaled into XML.
My Question
Is it necessary or recommended to additionally encode / decode the content field with base64?
TL;DR
To the best of my knowledge, you are not going against any good practice with your current implementation. One might question the design (exchanging files in JSON ? Storing binary inside XML ?), but this is a separate question.
Still, there is room for possible optmization, but the toolset you use (e.g. Spring rest template + Spring Controler + JSON serialization (jackson) + XML using JAXB) kind of hide the possible optimizations from you.
You have to carrefully weight the pros and cons of working around your comfortable "automat(g)ical" serializations that work well as of today to see if it is worth the trouble to tweak it.
We can nonetheless discuss the theory of what could be done.
A discussion about Base64
Base64 encoding in an efficient way to encode binary data in pure text formats (e.g. MIME strucutres such as email or some HTTP bodies, JSON, XML, ...) but it has two costs : the first is a non negligible size increase (~ 33% size), the second is CPU time.
Sometimes, (but you'd have to profile, check if that is your case), this cost is not negligible, esp. for large files (due to some buffering and char/byte conversions in the frameworks, you could easilly end up using e.g. 4x the size of the encoded file in the Java Heap).
When handling 10kb files at 10 requests/sec, this is usually NOT an issue.
But 10MB files at 100 req/second, well that is another ball park.
So you'd have to check (I doubt your typical server will reach 100 req/s with 10MB files, because that is a 1GB/s incoming network bandwidth).
What is optimizable in your current process
In your current process, you have multiple encodings taking place : the client needs to Base64 encode the bytes read from the file.
When the request hits the server, the server decodes the base64 to a byte[], then your XML serialization (JAXB) reconverts the byte[] to base64.
So in effect, "you" (more exactly, the REST controler side of things) decoded base64 content, all for nothing because the XML side of things could have used it directly.
What could be done
A few things.
Do you need base64 at the calling site ?
First, you do not have to encode at the client side. When using JSON, there is no choice, but the world did not wait for JSON to exchange files (e.g. arbitrary binary content) over HTTP.
If your content is a file name, a MIME type, and a file body, then standard, direct HTTP calls with no JSON at all is perfectly fine.
The MIME type could be mapped to the Content-Type HTTP Header, the file name inside the Content-Disposition HTTP header, and the contents as the raw HTTP body. No base64 needed (but you need your server-side to accept raw HTTP content as is). This is standard as can be.
This change would allow you to remove the encoding (client side), lower the network size of the call (~33% less), and remove one decoding at the server side. The server would just have to base64 encode (once) a raw stream to produce the XML, and you would not even need to buffer the whole file contents for that (you'd have to tweak you JAXB model a bit, but you can JAXB serialize directly bytes from an InputStream, which means, almost no buffer, and since your CPU probably encodes faster than your network serves content, no real latency incurred).
If this, for some reason, is not an option, let's say your client has to send JSON (and therefore base64 content)
Can you avoid decoding at the server side
Sort of. You can use a server-side bean where the content is actually a String and NOT a byte[]. This is hacky, but your REST controler will no longer deserialize base64, it will keep it "as is", which is a JSON string (which happens to be base64 encoded content, but the controler does not care).
So your server will have saved the CPU cost of one base64 decoding, but in exchange, you'll have a base64 String in java heap (compared to the raw byte[], +33% size on Java >=9 with compact strings, +166% size on Java < 9).
If you are to profit from this, you also have to tweak your JAXB to see the base64 encoded String as a byte[], which is not trivial as far as I can tell, unless you modify the JAXB object in such a way that it accepts a String instead of the byte[] which is kind of hacky (if your JAXB objects are generated from a XML schema, this might really become a pain to implement)
All in all this is much harder - probably too much if you are not really hitting the wall, performance wise, on this particular issue.
A few other stuff
Are your files pure binary, or are they actually text ? If there are text, you may benefit from using CDATA encoding on the XML side instead of base64 ?
Is your XML actually a SOAP call ? If so, and if the service supports MTOM, you could avoid base64 completely, but that is an altogether different subject.

How can I receive a large string in Java Servlet?

I am building a JSP application. I am trying to send an screenshot image from the page to the servlet by using base64 encoding. It makes the returning string super long with 100k characters length. So when I post this to the servlet and with getParameter, I am getting null there.
Is there a way to get them by chunks are am I missing something?
I found this maybe useful for you.
Passing a huge String as post parameter to a servlet
Namit Rana:
We used GZip compression/decompression to lower the size of the string. And it worked effectively.
So, the .net service compressed the huge string, sent it to our Servlet. We then decompress it at our server.

Spring SOAP Web service - Displaying images from DB/path

From my understanding of SOAP, it's all about request and and response and it always outputs the response in a cryptic format (XML).
However, a fellow student told me to create a SOAP Service that displays images from a DB (or a folder with images) - just like we know from Tumblr/Pinterest/etc.
I just don't understand how that's possible - if at all?
I think what they mean is storing the images in a folder somewhere on the server and storing the file path to them as strings in the database. At least that's how I would do it.
You could also possibly encode the data with something like base64 and store that plus other relevant information like the content type (jpeg/gif etc) + a filename for the file to later stick together/decode to form an image.
Also, just in my personal opinion SOAP/XML is the devil, and I would personally serve the data as JSON.

Grabbing data from web service xsd:base64Binary field in Java

I have a web service I am trying to call from Java. The XSD for the service defines a field as an xsd:base64Binary. I am using maven jaxb2 plugin to generate Java artifacts. The field becomes a byte[] in the generated Java object. The data that comes back in that field is either CSV or XML data depending on what is passed into the service. SoapUI displays the data perfectly (not encoded). Watching the wire with wireshark I can also see the non encoded data. My question is, how do I grab this data as a string in Java? I want to take this data and later write it into a file.
Response looks something like this:
Service Agreement,Interval Start Time,Interval End Time,Quantity,Unit of Measure .... etc.
Relevant bit of XSD:
Relevant bit of generated java:
protected byte[] greenDoc;
In my client java code I have been trying every possible combination of new String(byte[]), new String(byte[], charset), Base64 decoding, etc. and I just cannot seem to get the data correctly. I know it is not a limitation of the web service because like I said SoapUI can display the data perfectly.
Any pointers on how the client code can take the byte array and convert to string? Thanks!
Programmatically, you can use DatatypeConverter

How to serialise msgpack over http

tl;dr: Is there an efficient way to convert a msgpack in Java and C# for transport over HTTP.
I've just discovered the msgpack data format. I use JSON just about everything I send over the wire between client and server (that uses HTTP), so I'm keen to try this format out.
I have the Java library and the C# library, but I want to transport msgpacks over HTTP. Is this the intended use, or is it more for a local format?
I noticed a couple of RPC implementations, but they're whole RPC servers.
Thoughts?
-Shane
Transport and encoding are two very different things and it's entirely up to you to choose which transport to use and what data encoding to use, depending on the needs of your application. Sending msgpack data over HTTP is a perfectly valid use case and it is possible, but keep in mind the following two points:
msgpack is a binary encoding, which means it needs to be serialized into bytes before sending, and deserialized from the received bytes on the other end. It also means that it is not human-readable (or writable, for that matters) so it's really hard to inspect (or generate by hand) the HTTP traffic.
unless you intend to stream msgpack-encoded data over HTTP, you'll incur a fairly high overhead cost since the HTTP header size will most likely greatly overshadow the size of the data you're sending. Note that this also applies to JSON, but to a lesser extent since JSON is not as efficient in its encoding.
As far as implementation goes, the sending side would have to serialize your msgpack object into a byte[] before sending it as the request body in your HTTP request. You'll need to set the HTTP Content-Type to application/x-msgpack as well. On the receiving end, read the request body from the input stream (you probably can get your hands on a ByteArrayInputStream and deserialize into your msgpack object).

Categories

Resources