Im trying to generate a QR code using QRGen, encode it in Base64 and insert it as an image in an HTML string. Later, the HTML string is decoded to be displayed in a JEditorPane (and then sent to a printer). To this end, the ImageView class is extended and a custom View factory is used. This all works fine... sometimes. It completely depends on the input string. Some strings work without issue, others fail cause the decode process to fail with the error java.lang.IllegalArgumentException: Input byte array has wrong 4-byte ending unit.
Here is the encode process:
public BufferedImage generateQRCodeImage(String barcodeText) throws Exception {
ByteArrayOutputStream stream = QRCode.from(barcodeText).to(ImageType.PNG).stream();
ByteArrayInputStream bis = new ByteArrayInputStream(stream.toByteArray());
return ImageIO.read(bis);
}
public static String encodeToString(BufferedImage image, String type) {
String imageString = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ImageIO.write(image, type, bos);
byte[] imageBytes = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
imageString = encoder.encodeToString(imageBytes);
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
return imageString;
}
and the decode process:
private Image loadImage() {
String b64 = getBASE64Image();
BufferedImage newImage = null;
ByteArrayInputStream bais = null;
try {
bais = new ByteArrayInputStream(Base64.getDecoder().decode(b64.getBytes())); //fails here
newImage = ImageIO.read(bais);
} catch (Throwable ex) {
ex.printStackTrace();
}
return newImage;
}
#Override
public URL getImageURL() {
String src = (String) getElement().getAttributes().getAttribute(HTML.Attribute.SRC);
if (isBase64Encoded(src)) {
this.url = BASE64ImageView.class.getProtectionDomain()
.getCodeSource().getLocation();
return this.url;
}
return super.getImageURL();
}
private boolean isBase64Encoded(String src) {
return src != null && src.contains("base64,");
}
private String getBASE64Image() {
String src = (String) getElement().getAttributes().getAttribute(HTML.Attribute.SRC);
if (!isBase64Encoded(src)) {
return null;
}
return src.substring(src.indexOf("base64,") + 7, src.length() - 1);
}
And here is the QR code in question that fails to decode.
<img width='30' height='30' src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAH0AAAB9AQAAAACn+1GIAAAApklEQVR4Xu2UMQ4EMQgD/QP+/0vK6zjsvayUMmavWxQpMAUBkwS12wcveAAkgNSCD3rR5Lkgoai3GUCMgWqbAEYR3HxAkZlzU/0MyBisYRsgI1ERFfcpBpA+ze6k56Cj7KTdXNigFWZvSOpsgqLfd18i2aAukXh9TXBNmdWt5gzA/oqzWkkN8HtA7G8CNOwYAiZt3wZixUfkA32OHNQq7Bxs9oI/gC/9fV8AVCkPjQAAAABJRU5ErkJggg=='/>
I did open the above QR in a browser (Chrome) and it does work, which definitely points to something being wrong in the decode process and not the encode process.
Found the issue. In getBASE64Image(), I have
private String getBASE64Image() {
String src = (String) getElement().getAttributes().getAttribute(HTML.Attribute.SRC);
if (!isBase64Encoded(src)) {
return null;
}
return src.substring(src.indexOf("base64,") + 7, src.length() - 1);
}
The "-1" in the substring call was the cause of my problems. Not sure why this would work only sometimes, but removing seems to have fixed the problem.
Related
Generally, I am using below code to take a screenshot and attach in allure report :
#Attachment(value = "Page Screenshot", type = "image/png")
public static byte[] saveScreenshotPNG(WebDriver driver) {
return ((TakesScreenshot)driver).getScreenshotAs(OutputType.BYTES);
}
But now my need is I have already some screenshot on my desktop and want to attach it with an allure report. is that possible?
You can take the existing image and convert it to byte[]. getScreenshotAs() decodes the screenshot string so you might need to do it as well
Java
#Attachment(value = "Page Screenshot", type = "image/png")
public static byte[] saveScreenshotPNG(String path) {
File file = new File(path);
BufferedImage bufferedImage = ImageIO.read(file);
byte[] image = null;
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
ImageIO.write(bufferedImage, "png", bos);
image = bos.toByteArray();
} catch (Exception e) { }
// if decoding is not necessary just return image
return image != null ? Base64.getMimeDecoder().decode(image) : null;
}
Python
with open(path, 'rb') as image:
file = image.read()
byte_array = bytearray(file)
allure.attach(byte_array, name="Screenshot", attachment_type=AttachmentType.PNG)
So, I am downloading the profile picture from the Google SIgn-in api and I save it to a hidden file. The problem is that when I try to retrieve it, it throws me: D/skia: --- Failed to create image decoder with message 'unimplemented'. However when I retrieve an image from FireBaseStorage and save that one to the hidden file I can retrieve it whithout any problems.
I tried BitmapFactory.decodeByteArray(), but then I had a message telling me skia wasn't able to decode the file and it returned null.
The method I use to retrieve the profile picture and call the method that will save the file
private void getUsersPic() {
Bitmap profilePic;
try {
InputStream in = new URL(AppData.getUser().getPicture()).openConnection().getInputStream();
profilePic = BitmapFactory.decodeStream(in);
int size = profilePic.getRowBytes()*profilePic.getHeight();
ByteBuffer b = ByteBuffer.allocate(size);
byte[] bytes = new byte[size];
profilePic.copyPixelsToBuffer(b);
b.position(0);
b.get(bytes, 0, bytes.length);
SaveBitmapToFile.saveBitmap(bytes , AppData.getUser().getName()+AppData.getUser().getLastName());
} catch(Exception e) {
System.out.println("Get profile pic: "+e.toString());
}
}
Save the file
public static void saveBitmap(byte[] bitmap, String key) {
String path = AppData.getAppContext().getFilesDir()+"/.image"+"/";
File fileDir = new File(path);
if(!fileDir.isDirectory())
fileDir.mkdirs();
try {
File bitmapDir = new File(fileDir+"/"+key);
bitmapDir.createNewFile();
FileOutputStream stream = new FileOutputStream(bitmapDir);
stream.write(bitmap);
stream.close();
} catch (IOException e) {
System.out.println("Problem creating file "+e.toString()+ " Directory: "+fileDir);
}
}
Retrieve and return a bitmap
public static Bitmap getBitmap(String key) {
File file = new File(AppData.getAppContext().getFilesDir()+"/.image/"+key);
try {
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
return BitmapFactory.decodeStream(buf);//BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
} catch(Exception e) {
System.out.println("Exception getting bitmap: "+e.toString());
return null;
}
}
The last method should return a Bitmap and it is doing it. It is just not working when the image comes from the Google Sign-in api.
As pskink said in the comment of the post, I had to use compress() instead of copyPixelToBuffer(). Here is my updated method:
private void getUsersPic() {
Bitmap profilePic;
try {
InputStream in = new URL(AppData.getUser().getPicture()).openConnection().getInputStream();
profilePic = BitmapFactory.decodeStream(in);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
profilePic.compress(Bitmap.CompressFormat.PNG, 100, stream);
SaveBitmapToFile.saveBitmap(stream.toByteArray() , AppData.getUser().getName()+AppData.getUser().getLastName());
} catch(Exception e) {
System.out.println("Get profile pic: "+e.toString());
}
}
I have a String which is first zipped (not gzip) and then base64 encoded.
I want to have the plain text again. Following code
private void decode_decompress(String string) {
byte[] decodedBytes = Base64.decodeBase64(string.getBytes());
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Inflater decompresser = new Inflater(true);
InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(stream, decompresser);
try {
inflaterOutputStream.write(decodedBytes);
inflaterOutputStream.close();
byte[] output2 = stream.toByteArray();
logger.info("Data: {}", output2.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
throws an exception:
java.util.zip.ZipException: invalid stored block lengths
at java.util.zip.InflaterOutputStream.write(InflaterOutputStream.java:273)
at java.io.FilterOutputStream.write(FilterOutputStream.java:97)
at de.martinm.tools.EBICS.DSTools.decode_decompress(DSTools.java:87)
at de.martinm.tools.EBICS.DSTools.process(DSTools.java:77)
at de.martinm.tools.EBICS.DSTools.main(DSTools.java:100)
I guess, I'm mixing up again input/output inflate/deflate
Here is the data which is compressed and base64 encoded:
eJx1U2tzqjAQ/SsO96NTw1PBQTr4qlgtyqvVL3cihEehiSYg1l9/rd46dZx+y549Ocme3dUfDx9FY48oywjucUKL5xoIhyTKcNLjfG/8oHINVkIcwYJg1OMw4R4N3c0SDMuKokW1eUafNo0QHcISNk5qmPW4tCy3XQDqum6hTRayFqEJcHle4C6MbnRLqqUzQ+R5HvAaOHEiliV/vtlnjR5XUdw90S5hd8Lz8jfhwLJf9ATwNp+5YYo+4EOGvyoJ0ekWy7rsDM5ICMtz7b/+uXH/Ljgf/7JvG1oHFnF3tlg4JoZ+OQewqJChR6zruOZNPCdRVVTMMOebJcxHZRJ1kqeDJJqfR6IQJDdngt1cBt5ncYKnO8d99Tp9gYoweT2O40BUatURhWKZvVHV7E8102XHXTDN5ZI1vZyX6KKeSm+SmK9VlQZ5nZeKvd8X7aPUmRztxdp8rtaZom1kJlsRqsK95RSS7RJ7AYOQbg6S2vZXrjWA6S5vqzlWYCG/z947YgXjcOasFuF8/JKs34nngCGYIVBukJd9jLHftuQSmfV6LJFg2CQrU5Ze4qJYpR1/b5qD2MaOvSv27Z1PV4GA+p1U1IDFWLJaifGEKmGKxZ3lq5Ox0EHb1G++JvGIpaSayxYd9J2kfO7nhXiw4XYYD3fyJsbC8kmDVv2iJZqqaAtnn/d08MPkL8NHh+1plHFpmEtzcM5ekXN00yBw075rg4PLxhgmz7d1cAf/gG5GAdISI2oNjVHfGried5K/QrrPfqYUHfwH7sSu62b8A39iR+Y=
This solved the issue:
private void decode_decompress(String string) {
byte[] decodedBytes = Base64.decodeBase64(string);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Inflater decompresser = new Inflater(false);
InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(stream, decompresser);
try {
inflaterOutputStream.write(decodedBytes);
inflaterOutputStream.close();
String data = stream.toString();
logger.info("Data: {}", data);
} catch (IOException e) {
logger.error(string, e);
}
}
I need to send pictures from raspberry pi (written in python) to server (in java). I have read that the easiest way is to encode image to base64 send it and decode on the server side.
Now I have a generated base64 txt file from png.
I need a program to decode it.
I have folder in which I have my base64 code(64kb in one line of txt file)
encoding program that looks like
import urllib
encoded = urllib.quote(open("/home/wojtek/Desktop/wideo/nazdjecia/test_image1.png", "rb").read().encode("base64"))
print encoded
f = open("path/obr.txt",'w')
f.write(encoded)
f.close()
print "done"
and program that looks like this
public class JavaApplication2 {
public static BufferedImage decodeToImage(String imageString)
{
BufferedImage image = null;
byte[] imageByte;
try
{
BASE64Decoder decoder = new BASE64Decoder();
imageByte = decoder.decodeBuffer(imageString);
ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
image = ImageIO.read(bis);
bis.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return image;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
try {
BufferedReader in = new BufferedReader(new FileReader("/filepath/obr.txt"));
String str;
str = in.readLine();
NewJFrame gui = new NewJFrame();
gui.setVisible(true);
BufferedImage im = decodeToImage(str);
ImageIcon ico = new ImageIcon(im);
gui.imgset(ico );
// str is one line of text; readLine() strips the newline character(s)
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
when I try to decode it with java it crashes
Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
javax.imageio.IIOException: Error skipping PNG metadata at
com.sun.imageio.plugins.png.PNGImageReader.readMetadata(PNGImageReader.java:680)
at
com.sun.imageio.plugins.png.PNGImageReader.readImage(PNGImageReader.java:1229)
at
com.sun.imageio.plugins.png.PNGImageReader.read(PNGImageReader.java:1577)
at javax.imageio.ImageIO.read(ImageIO.java:1448) at
javax.imageio.ImageIO.read(ImageIO.java:1352) at
javaapplication2.JavaApplication2.decodeToImage(JavaApplication2.java:33)
at javaapplication2.JavaApplication2.main(JavaApplication2.java:55)
Caused by: java.io.EOFException at
javax.imageio.stream.ImageInputStreamImpl.readInt(ImageInputStreamImpl.java:251)
at
com.sun.imageio.plugins.png.PNGImageReader.readMetadata(PNGImageReader.java:666)
... 6 more java.lang.NullPointerException at
javax.swing.ImageIcon.(ImageIcon.java:228) at
javaapplication2.JavaApplication2.main(JavaApplication2.java:56)
what am I doing wrong... :(
I am trying to read a JSON file from assets in fragment using getAssets(), but the IDE says "can not resolve this method (getAssets())".
Code
public String loadJSONFromAsset() {
String json = null;
try {
InputStream is = getAssets().open("moods.json");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
json = new String(buffer, "UTF-8");
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
return json;
}
Try this:
Create a class LoaderHelper.java
public class LoaderHelper {
public static String getJson(Context context, String json){
String jsonString=parseFileToString(context, json);
return jsonString;
}
public static String parseFileToString( Context context, String filename )
{
try
{
InputStream stream = context.getAssets().open( filename );
int size = stream.available();
byte[] bytes = new byte[size];
stream.read(bytes);
stream.close();
return new String( bytes );
} catch ( IOException e ) {
Log.i("GuiFormData", "IOException: " + e.getMessage() );
}
return null;
}
}
To get the json string call the above class as:
String str=LoaderHelper.parseFileToString(context, "levels.json");
where levels.json is the json file stored in asset folder.
Method getAssets() is part of Context class. So if you're not calling loadJSONFromAsset() in the class that extends Context, e.g. Activity, you need to provide reference to Context as an argument to your method and then call getAssets() on it