Same Image, but different base64 - java

I used the following function to create a base64 encoded string of my Gravatar image (https://www.gravatar.com/avatar/cd5415f97afbe0177ba35ae31fbfd0db):
final BASE64Encoder encoder = new BASE64Encoder();
String encoded = encoder.encode(inputStreamToByteArray(is));
encoded = encoded.replaceAll("\r?\n", "");
return encoded;
I ran the method a couple of days ago and got the following base64 encoded string:
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
Just today I executed the method again, using the same image. But instead of retrieving the same encoded string, I received:
/9j/4AAQSkZJRgABAQAAAQABAAD//gA7Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2NjIpLCBxdWFsaXR5ID0gOTAK/9sAQwADAgIDAgIDAwMDBAMDBAUIBQUEBAUKBwcGCAwKDAwLCgsLDQ4SEA0OEQ4LCxAWEBETFBUVFQwPFxgWFBgSFBUU/9sAQwEDBAQFBAUJBQUJFA0LDRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgAUABQAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A7j4ZeIl1PRljvrcCMgorw3Qdk78qBnb6Zzjiusv4o7nS45w3lzwuY2heQOxX+Fs4FeM219c+EvF7TzyXd1bCVWlggl3ERYJHyAZAB9q908Iax4f8d6Mo0y4ikmlTcs7N5jqw6Fsj1GMEdq/JsPXq5XmcK1T4L8r8uh6biqlOyPH/AInfGjwr8JRajxDeNDNdKzRQxRl2KjqTjoPr159K83h/bK8F3koEMNyYmIHm4OBzznj3Feefte61rPh3xno+h+IC3jbQ4ZFvfs+qt5csOWKvGjQ7AqsF7Lx719x/s4/snfs5+J/COn+KvD/h2PW7S/jSU2+o3klyttJgFo2jLlAyng5B6V+3xqqK+FP+vU8SSbejseZeD/iBonju3MulXQkYDJiYYYD1x+NdGy11n7Vvgr4Y/Baz8LaloGjWGgeI9R1WCyhh04iFJoT8spaJflwAw+YAHO3JxweVcEcEYNVJxdnHqaQcmrSK8i1C1WH6VXc4rN7GhXm4FZsxyxq/OeTWdO3WsJbG0Rnis3Okazp2p2MIlupGFvny1bAJ5B3Y49a9J0w6IiWNwmkWH9rXCRs81tAV2MfvL82Sq54znjNcn4i0xtU0aaGN44peqyS8Kp9c11Pw9msPh54OFlG3kpvSeK7IEayll+bk8MzFT9MjA4zX5vxDSo4bE+0n9rout9H8v8zfDyc6fL2PnX4r/s/eMf2ovjbrZ8M6fHDpWkCLTJ9UmmCWySpHucFsZZt7MPlBPTtV3S/2fvHP7Klld3Ok/Fkafcy4d9O0y1eZbiQA7Qcjpk4yVxyT2r6g+HPiY+HfhDpflxi2m1G6u7y68vkiaW4lZ9x/vAnafTbisb4rW9r4q8AS3MM3l3Vq5eGfcAyt79cqfSvTw2Z1/bU8FS0jFJXtduy7vuctSlFRdR7ny3ZeCdT+InxAsvFHxD8Zv4iu4JEkjgXfgdCqfMF2rnqFHb3r6EnIdy4wQ3Py9K8HtfHi3ypHd+WZ1OElUg9PXH0Nen+C/GVvrNubOYeXMANhK4OSM/lnPPv7V6axNeljFKvK8WrbWsYwceWyN+U8VVkOKmmYhiCMEcYNVJWr6Zu5ZBM3BrOlPNXJ2496pTGspm0TtY1BUhlDKeoPQ15R4k8dal4Mu3GsaqF014wsEcsBSCKTeQSXU5yFOQOScDoOvrKHI4rxf4pahDomvT3Gt6LNqthLJFb2d08n7myJBdmClWByV5XAznBJFeRmeDhi5U5yjflf9fkZ0ZuF7HqPgPxSbvwD/Z9zfJeairNqAbzzI0kcrs24k8g794IPIwPUVzfjTxpdS+CNf8PRzPZy36A215CAWgkGAcg9VI6jjpwaw9I+O/hDUdJhuZtXaDURI7RqlgUW4UkiSN0H3WY7SCuQCi8DJy/xXbpOgeNHXeA+yRSrrx0IPINfF45VssxntI/BLVPez6o6Y8tWOu54F4SvdH03xDLp3iy51C0aIZA0yFWklJxtK72ACn8enfGa+gPDfjz4bz2k1joOg65LqYysd1qV5GGiUgDeqqDnaeccjk59K8q8TeFdK16JbbU4ZBMuTFdQsVkiPqpH8q4qXStT8JbLie4knt42Hl39uAJIj2Miqcgf7S+vIFfXYbMaWNharucE6Lpv3T62s9Wub6ZoL1YzcxoMSxKV81QF+ZlydrcjoSDwQSDUsqkVwfwm+JC+KQulX80VvrhhMNlfBsQXik7hExHqQGAI4PzL1YN6ArrcQrIudrDIyCD+R6V7dKV427FRdzPnzVKXmtSaIHNUZosZqpI1TsdfG+OD0rM8T+O9J03xBo2mXmlyHTZkP2rzokljkUbhkNuVtx3DIxwAMGtACuQ+K+n6lf8AhZf7Md0khmEkuwDJi2sGGeoHIzjrjuK58Wpui3TdmjGm7M8e8X6TZ+JY4FTT2sLBrt50N8uyJRkFQSpyjMB03HntyceoTeG08SeALSTxDqUtjdWany9aifypGjVvkeQEkHK4BBznrwTXgnjr4ua1FrE17Frs6WsamM2M8xaMgE5QpwuByPlA7V6j8GvH9n40eyHiPEWr+WrabalAtqFAOTGAAvmfKeTz2B7V4+CVKo5q10901pf8TaV0SeFvBGseIJpvt+TpasFhvri38iWde7CLcSP+BYz19q3734MWpjbyNVkddpXyZk4I9O9ejNmoJM4reOTYSEueCafk3+WxDqtqzPlLWvhB4u8A3kt/oqJqFlvLvp7ggbQc5UjoRycfiOenvXgPxnJ4u0CCa8hlttRVAJIpyCzD1yOG9Mj275rq3YiuS1Kym8P3TX2no76fK4e8souo/wCmkYHf1UD1wDyrevQoqm/el8/8/wDMwcmtjflkIqjcS8GmxX8V2qNFKk0Uq+ZDMhBWVPUe4yMjtkdQQTHOCTXTKLjuNSO7WIYpxiBBBGR6GrCxCn+XWVy7Hyj8WfhlZX3jK4sRY4gb5lYyAB2ZSc5zwecYIxgVg+HfhreaHqtpeaJrNnAY7hZ4rS6ckHG1mIPbgA4AJO0cCvor4vaPI+m2+o28YMsTeVK7PtVUPIZuDwDnnj72OQa86svDGm3FpHcXEz3+pO8bxf6RujR1fIaRVQuUGOQOoI46Gvgsfia2Aqypxfu9PmdtOKmrnuaATQRyKyuHUMGToc9x7VE8eKg8K30eo6awW6iunjc58tw2FPK57j6MAeOlackXtX2uFrrE0Y1V1RxTg4yaMiWPFVJIyK2JYfaqcsOK67mRwXiLw9c2gku9KZkUv5r2yDOyTr5sY9TzuTo4J7k7n+G/EUXiO1k+6l3AQs0SnIB7Mvqp7fiOoNdhJB6VzsfhC3tPEkmsW/7mSeIxzxAfK5znd7HPX/Ek0X0sFj//2Q==
As you may notice, the first couple of characters are the same, but if you look at the end of the strings, you notice directly that they are completely different. Why is that? The image is still my Gravatar image (e.g., using http://codebeautify.org/base64-to-image-converter shows the same image for both strings).
Thanks for any hint, explanation! Is there anything wrong with my implementation? Is some of the implementation time or location dependent? How can I get the same base64 string for the same image?
PS: inputStreamToByteArray is implemented as follows:
public static byte[] inputStreamToByteArray(final InputStream is) {
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
final byte[] data = new byte[16384];
try {
int nRead;
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
} catch (final IOException e) {
return null;
} finally {
try {
buffer.close();
} catch (final IOException e) {
// ignore
}
}
return buffer.toByteArray();
}

Converting both images back to .jpg, and using http://regex.info/exif.cgi, the following header comments appear:
One:
CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 90
Other:
CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 90
(can't remember which order I converted the images in, but there is a version change either way)
As you can see, they have upgraded the JPEG compressor (or spec?) to a newer version, resulting in a different JPEG encoding, or at least difference in the comments.
So to answer your question, there does not seem to be an issue with your base64 converter, but just that the two images actually are different

Related

Compressing a string and storing it on a database as string for later decompression

I have a huge string that I need to cache somewhere and since I cannot write to file my only option is to store this on the data base as text, more specifically, in the clob I have I'm storing a JSON file where I'm placing the compressed string under a certain key of that JSON object.
I'm compressing the strings but somewhere across the string manipulation something happens that doesn't allow me to decompress the data, so I'm wondering if I should encode the data to base 64 but that will lose compression.
What could I do to ensure I can store the compressed string in the database so I can later fetch it?
I cannot change the database, so I'm stuck with that CLOB field
These are my compression functions:
public static String compress(String text) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
OutputStream out = new DeflaterOutputStream(baos);
out.write(text.getBytes("UTF-8"));
out.close();
} catch (IOException e) {
//ooops
}
return baos.toString();
}
public static String decompress(String bytes) {
InputStream in = new InflaterInputStream(new ByteArrayInputStream(bytes.getBytes()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
byte[] buffer = new byte[8192];
int len;
while ((len = in.read(buffer)) > 0)
baos.write(buffer, 0, len);
return new String(baos.toByteArray(), "UTF-8");
} catch (IOException e) {
//ooops
}
}
As you found out, you can't store binary data in a CLOB without some corruption, so encoding to text will be required.
Base 64 will, on average add 33% to the size of your binary data. So you will lose some compression, but if your compression ratio is greater than 25% (this is often easy with particular types of text strings), then compression followed by base 64 encoding may provide you with a net storage gain. Lots of CPU use though.....
You can't convert arbitrary binary data to a String without breaking it. As you've already stated, if you want to store the data in a clob, you need to base64 encode the data (or use some other valid binary to text encoding).
Have you thought of other solutions, such as using memcached or other caching system? Or do you really want to mess around with compression?

Where to start in building an app/system that compresses and decompresses a file

I'm currently developing an application in Java/Android that allows the user to compress and decompress files. At first, I started to study the file size such as:
1Byte = 8Bits
1KB = 1024Byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB
1EB = 1024PB
1ZB = 1024EB
1YB = 1024ZB
After I studied this, I studied and read some articles on the net and found out there are 2 types of file compression (Correct me if I'm wrong): Lossless and Lossy. Lossless compression means that a file is compressed into a smaller bit without losing any single file while lossy compression means that important files were being removed while compressing the file.
I also read that compression(run-length coding method) is like this:
AAABBCCDFFFFEEEEH
to this:
3A2B2CD4F4EH
which gives me an idea on how compressing/decompressing works on file.
I also searched the net that there is an API for compressing file on java(also applicable on android) which is
java.util.zip
I also tried some codes on compressing and decompressing file from various helpful websites/forum/etc (including stackoverflow.com) which gives me an experience to this study.
I also read about algorithms used in data compression which are
Huffman encoding algorithm - assigns a code to characters in a file based on how frequently those characters occur
run-length encoding - generates a two-part value for repeated characters: the first part specifies the number of times the character is repeated, and the second part identifies the character
Lempel-Ziv algorithm - converts variable-length strings into fixed-length codes that consume less space than the original strings.
Now, I need to know how to code an algo in compressing and decompressing file by using java.util.zip(I also don't know how to use it. tutorials on net is not working for me :/). What algo does winzip, winrar, compressed folder(windows), and androzip(android app) is using? Will someone please teach me step by step(treat me as an unschooled person) on how java.util.zip works and the different algorithms. sorry for the long post folks. Thanks for the future help and posts(if there will be)!
public static final byte[] unzip(byte[] in) throws IOException {
// decompress using GZIPInputStream
ByteArrayOutputStream outStream =
new ByteArrayOutputStream(EXPECTED_COMPRESSION_RATIO * in.length);
GZIPInputStream inStream =
new GZIPInputStream ( new ByteArrayInputStream(in) );
byte[] buf = new byte[BUF_SIZE];
while (true) {
int size = inStream.read(buf);
if (size <= 0)
break;
outStream.write(buf, 0, size);
}
outStream.close();
return outStream.toByteArray();
}
public static final byte[] zip(byte[] in) {
try {
// compress using GZIPOutputStream
ByteArrayOutputStream byteOut=
new ByteArrayOutputStream(in.length / EXPECTED_COMPRESSION_RATIO);
GZIPOutputStream outStream= new GZIPOutputStream(byteOut);
try {
outStream.write(in);
} catch (Exception e) {
LOG.error("", e);
}
try {
outStream.close();
} catch (IOException e) {
LOG.error("", e);
}
return byteOut.toByteArray();
} catch (IOException e) {
LOG.error("", e);
return null;
}
}

PNG read and write with Java increases file size

Got a problem when reading and writing a png file. I read it with ImageIO to a byte array and then write this byte array again using ImageIO. But the file size increases significantly. How can this happen?
public BufferedImage toBufferedImage(InputStream inputstream) {
try {
return ImageIO.read(inputstream);
} catch (Exception e) {
throw new IllegalStateException("Can't convert to buffered image", e);
}
}
public byte[] toByteArray(BufferedImage bufferedImage, String filetype) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
ImageIO.write(bufferedImage, filetype, output);
return output.toByteArray();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
Follow up: is there any library to support compressed PNGs that is written in Java and does not need any native code?
This is most likely due to the compression algorithm being different between Java and whatever created the original PNG.
The documentation says it's decoding the input file, so it's not being held in memory as a PNG:
Returns a BufferedImage as the result of decoding a supplied File with an ImageReader chosen automatically from among those currently registered. The File is wrapped in an ImageInputStream. If no registered ImageReader claims to be able to read the resulting stream, null is returned.
When it writes it back, it has to re-encode the PNG file, and Java's PNG encoding doesn't seem to be as efficient as whatever created your original file.
The PNG writer supplied with the JDK does not support compression. You can quickly check this with:
w = ImageIO.getImageWritersByFormatName("png").next();
p = w.getDefaultWriteParam();
print("Can compress? "+p.canWriteCompressed());
// Can compress? false
It's possible that imageio-ext or jai-imageio include a png writer with compression support: http://java.net/projects/imageio/

Out of memory when encoding file to base64

Using Base64 from Apache commons
public byte[] encode(File file) throws FileNotFoundException, IOException {
byte[] encoded;
try (FileInputStream fin = new FileInputStream(file)) {
byte fileContent[] = new byte[(int) file.length()];
fin.read(fileContent);
encoded = Base64.encodeBase64(fileContent);
}
return encoded;
}
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
at org.apache.commons.codec.binary.BaseNCodec.encode(BaseNCodec.java:342)
at org.apache.commons.codec.binary.Base64.encodeBase64(Base64.java:657)
at org.apache.commons.codec.binary.Base64.encodeBase64(Base64.java:622)
at org.apache.commons.codec.binary.Base64.encodeBase64(Base64.java:604)
I'm making small app for mobile device.
You cannot just load the whole file into memory, like here:
byte fileContent[] = new byte[(int) file.length()];
fin.read(fileContent);
Instead load the file chunk by chunk and encode it in parts. Base64 is a simple encoding, it is enough to load 3 bytes and encode them at a time (this will produce 4 bytes after encoding). For performance reasons consider loading multiples of 3 bytes, e.g. 3000 bytes - should be just fine. Also consider buffering input file.
An example:
byte fileContent[] = new byte[3000];
try (FileInputStream fin = new FileInputStream(file)) {
while(fin.read(fileContent) >= 0) {
Base64.encodeBase64(fileContent);
}
}
Note that you cannot simply append results of Base64.encodeBase64() to encoded bbyte array. Actually, it is not loading the file but encoding it to Base64 causing the out-of-memory problem. This is understandable because Base64 version is bigger (and you already have a file occupying a lot of memory).
Consider changing your method to:
public void encode(File file, OutputStream base64OutputStream)
and sending Base64-encoded data directly to the base64OutputStream rather than returning it.
UPDATE: Thanks to #StephenC I developed much easier version:
public void encode(File file, OutputStream base64OutputStream) {
InputStream is = new FileInputStream(file);
OutputStream out = new Base64OutputStream(base64OutputStream)
IOUtils.copy(is, out);
is.close();
out.close();
}
It uses Base64OutputStream that translates input to Base64 on-the-fly and IOUtils class from Apache Commons IO.
Note: you must close the FileInputStream and Base64OutputStream explicitly to print = if required but buffering is handled by IOUtils.copy().
Either the file is too big, or your heap is too small, or you've got a memory leak.
If this only happens with really big files, put something into your code to check the file size and reject files that are unreasonably big.
If this happens with small files, increase your heap size by using the -Xmx command line option when you launch the JVM. (If this is in a web container or some other framework, check the documentation on how to do it.)
If the file recurs, especially with small files, the chances are that you've got a memory leak.
The other point that should be made is that your current approach entails holding two complete copies of the file in memory. You should be able to reduce the memory usage, though you'll typically need a stream-based Base64 encoder to do this. (It depends on which flavor of the base64 encoding you are using ...)
This page describes a stream-based Base64 encoder / decoder library, and includes lnks to some alternatives.
Well, do not do it for the whole file at once.
Base64 works on 3 bytes at a time, so you can read your file in batches of "multiple of 3" bytes, encode them and repeat until you finish the file:
// the base64 encoding - acceptable estimation of encoded size
StringBuilder sb = new StringBuilder(file.length() / 3 * 4);
FileInputStream fin = null;
try {
fin = new FileInputStream("some.file");
// Max size of buffer
int bSize = 3 * 512;
// Buffer
byte[] buf = new byte[bSize];
// Actual size of buffer
int len = 0;
while((len = fin.read(buf)) != -1) {
byte[] encoded = Base64.encodeBase64(buf);
// Although you might want to write the encoded bytes to another
// stream, otherwise you'll run into the same problem again.
sb.append(new String(buf, 0, len));
}
} catch(IOException e) {
if(null != fin) {
fin.close();
}
}
String base64EncodedFile = sb.toString();
You are not reading the whole file, just the first few kb. The read method returns how many bytes were actually read. You should call read in a loop until it returns -1 to be sure that you have read everything.
The file is too big for both it and its base64 encoding to fit in memory. Either
process the file in smaller pieces or
increase the memory available to the JVM with the -Xmx switch, e.g.
java -Xmx1024M YourProgram
This is best code to upload image of more size
bitmap=Bitmap.createScaledBitmap(bitmap, 100, 100, true);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); //compress to which format you want.
byte [] byte_arr = stream.toByteArray();
String image_str = Base64.encodeBytes(byte_arr);
Well, looks like your file is too large to keep the multiple copies necessary for an in-memory Base64 encoding in the available heap memory at the same time. Given that this is for a mobile device, it's probably not possible to increase the heap, so you have two options:
make the file smaller (much smaller)
Do it in a stram-based way so that you're reading from an InputStream one small part of the file at a time, encode it and write it to an OutputStream, without ever keeping the enitre file in memory.
In Manifest in applcation tag write following
android:largeHeap="true"
It worked for me
Java 8 added Base64 methods, so Apache Commons is no longer needed to encode large files.
public static void encodeFileToBase64(String inputFile, String outputFile) {
try (OutputStream out = Base64.getEncoder().wrap(new FileOutputStream(outputFile))) {
Files.copy(Paths.get(inputFile), out);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

Java - Image encoding in XML

I thought I would find a solution to this problem relatively easily, but here I am calling upon the help from ye gods to pull me out of this conundrum.
So, I've got an image and I want to store it in an XML document using Java. I have previously achieved this in VisualBasic by saving the image to a stream, converting the stream to an array, and then VB's xml class was able to encode the array as a base64 string. But, after a couple of hours of scouring the net for an equivalent solution in Java, I've come back empty handed. The only success I have had has been by:
import it.sauronsoftware.base64.*;
import java.awt.image.BufferedImage;
import org.w3c.dom.*;
...
BufferedImage img;
Element node;
...
java.io.ByteArrayOutputStream os = new java.io.ByteArrayOutputStream();
ImageIO.write(img, "png", os);
byte[] array = Base64.encode(os.toByteArray());
String ss = arrayToString(array, ",");
node.setTextContent(ss);
...
private static String arrayToString(byte[] a, String separator) {
StringBuffer result = new StringBuffer();
if (a.length > 0) {
result.append(a[0]);
for (int i=1; i<a.length; i++) {
result.append(separator);
result.append(a[i]);
}
}
return result.toString();
}
Which is okay I guess, but reversing the process to get it back to an image when I load the XML file has proved impossible. If anyone has a better way to encode/decode an image in an XML file, please step forward, even if it's just a link to another thread that would be fine.
Cheers in advance,
Hoopla.
I've done something similar (encoding and decoding in Base64) and it worked like a charm. Here's what I think you should do, using the class Base64 from the Apache Commons project:
// ENCODING
BufferedImage img = ImageIO.read(new File("image.png"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "png", baos);
baos.flush();
String encodedImage = Base64.encodeToString(baos.toByteArray());
baos.close(); // should be inside a finally block
node.setTextContent(encodedImage); // store it inside node
// DECODING
String encodedImage = node.getTextContent();
byte[] bytes = Base64.decode(encodedImage);
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
Hope it helps.
Apache Commons has a Base64 class that should be helpful to you:
From there, you can just write out the bytes (they are already in a readable format)
After you get your byte array
byte[] array = Base64.encode(os.toByteArray());
use an encoded String :
String encodedImg = new String( array, "utf-8");
Then you can do fun things in your xml like
<binImg string-encoding="utf-8" bin-encoding="base64" img-type="png"><![CDATA[ encodedIImg here ]]></binImg>
With Java 6, you can use DatatypeConverter to convert a byte array to a Base64 string:
byte[] imageData = ...
String base64String = DatatypeConverter.printBase64Binary(imageData);
And to convert it back:
String base64String = ...
byte[] imageData = DatatypeConverter.parseBase64Binary(base64String);
Your arrayToString() method is rather bizarre (what's the point of that separator?). Why not simply say
String s = new String(array, "US-ASCII");
The reverse operation is
byte[] array = s.getBytes("US-ASCII");
Use the ASCII encoding, which should be sufficient when dealing with Base64 encoded data. Also, I'd prefer a Base64 encoder from a reputable source like Apache Commons.
You don't need to invent your own XML data type for this. XML schema defines standard binary data types, such as base64Binary, which is exactly what you are trying to do.
Once you use the standard types, it can be converted into binary automatically by some parsers (like XMLBeans). If your parser doesn't handle it, you can find classes for base64Binary in many places since the datatype is widely used in SOAP, XMLSec etc.
most easy implementation I was able to made is as below, And this is from Server to Server XML transfer containing binary data Base64 is from the Apache Codec library:
- Reading binary data from DB and create XML
Blob blobData = oRs.getBlob("ClassByteCode");
byte[] bData = blobData.getBytes(1, (int)blobData.length());
bData = Base64.encodeBase64(bData);
String strClassByteCode = new String(bData,"US-ASCII");
on requesting server read the tag and save it in DB
byte[] bData = strClassByteCode.getBytes("US-ASCII");
bData = Base64.decodeBase64(bData);
oPrStmt.setBytes( ++nParam, bData );
easy as it can be..
I'm still working on implementing the streaming of the XML as it is generated from the first server where the XML is created and stream it to the response object, this is to take care when the XML with binary data is too large.
Vishesh Sahu
The basic problem is that you cannot have an arbitrary bytestream in an XML document, so you need to encode it somehow. A frequent encoding scheme is BASE64, but any will do as long as the recipient knows about it.
I know that the question was aking how to encode an image via XML, but it is also possible to just stream the bytes via an HTTP GET request instead of using XML and encoding an image. Note that input is a FileInputStream.
Server Code:
File f = new File(uri_string);
FileInputStream input = new FileInputStream(f);
OutputStream output = exchange.getResponseBody();
int c = 0;
while ((c = input.read()) != -1) {
output.write(c); //writes each byte to the exchange.getResponseBody();
}
result = new DownloadFileResult(int_list);
if (input != null) {input.close();}
if (output != null){ output.close();}
Client Code:
InputStream input = connection.getInputStream();
List<Integer> l = new ArrayList<>();
int b = 0;
while((b = input.read()) != -1){
l.add(b);//you can do what you wish with this list of ints ie- write them to a file. see code below.
}
Here is how you would write the Integer list to a file:
FileOutputStream out = new FileOutputStream("path/to/file.png");
for(int i : result_bytes_list){
out.write(i);
}
out.close();
node.setTextContent( base64.encodeAsString( fileBytes ) )
using org.apache.commons.codec.binary.Base64

Categories