I am using the following test to read a file, base 64 encode it, then decode the base 64 back to a new image. I noticed that the new image file size (after the conversion) is significantly less than the original image leading me to think that somehow, part of the image data is being lost in the conversion. I can see the image but am worried about image quality. Any insight on what I might be doing wrong would be greatly appreciated.
Test class:
package test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
public class ImageTest { //should be B64Test
public static void main(String[] args) {
ImageTest imageTest = new ImageTest();
try {
BufferedImage img = null;
BufferedImage finalImg = null;
try {
// img = ImageIO.read(new File("/home/user/Desktop/test1.jpg"));
img = ImageIO.read(Files.newInputStream(Paths.get("/home/user/Desktop/test1.jpg")));
//encode base64 and print
final String base64encoded = ImageConverter.encodeToString(img, "jpeg");
System.out.println("read file " + base64encoded);
//convert base64 string to image
finalImg = ImageConverter.decodeToImage(b64encoded);
ImageIO.write(finalImg, "jpeg", new File("/home/user/Desktop/test2.jpg"));
} catch (IOException e) {
System.out.println("exception " + e);
}
} catch (Exception e) {
System.out.println("exception " + e);
}
}
}
ImageConverter
package test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;
public class ImageConverter {
public static String imgToBase64String(final RenderedImage img, final String formatName) {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ImageIO.write(img, formatName, Base64.getEncoder().wrap(os));
return os.toString(StandardCharsets.ISO_8859_1.name());
} catch (final IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
public static BufferedImage base64StringToImg(final String base64String) {
try {
return ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(base64String)));
} catch (final IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
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();
BASE64Encoder encoder = new BASE64Encoder();
imageString = encoder.encode(imageBytes);
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
return imageString;
}
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;
}
}
I can try testing both the base 64 encoder/decoder available in jdk8 as well as the one in sun.java.misc (which I realize I do not need to use). Any thoughts on what might be causing the image size to shrink (I would prefer doing that myself if needed using imagemagick or graphicsmagick etc.).
The original image was 1.2 MB (1,249,934 bytes) but the new image is 354.5 kB (354,541 bytes) - width/height is the same for both images.
As #JBNizet points out in his comment, the reason for the change in size (the size may grow as well, depending on the input image and compression settings), is that you are not just encoding/decoding binary data to/from Base64, you are also re-encoding the image data (two times) using JPEG encoding (with default encoding settings). Unless the original image was encoded with the exact same settings, you will lose some precision, and the file size will change.
Another likely reason for the decrease in file size, is that a BufferedImage does not carry any of the meta data contained in the original JPEG file. So your process of re-encoding the JPEG will also lose any Exif or XMP metadata, thumbnail, color profile etc. Depending on the source of the image, this may contribute to a significant part of the file size.
Again, as #JBNizet says, the best thing is to not involve ImageIO at all in this case, just use normal file I/O and encode the original bytes using Base64, and decode again to recover the original file contents exactly.
PS: If you intend on doing image processing on the image in between the Base64 encoding/decoding, you will of course need to decode the image data (using ImageIO or similar), but you should try to do it only once (for better performance), and perhaps look into preserving the meta data. Also, I think image encoding/decoding and Base64 encoding/decoding are separate issues, and should not be interleaved like it is now. Split it up, for a better separation of concerns.
Related
i want to compress a jpeg Image, running a java program from an pl/sql procedure within th oracle database.
I want to call the java class from pl/sql procedure with the blob (jpeg image), an the java program should return the compresses image in a blob.
I have found this code from https://examples.javacodegeeks.com/desktop-java/imageio/compress-a-jpeg-file/ an it works fine and the jpeg images are compressed. But this program used jpeg-Files in the file system, i need the original image and the compresse image as blob parameter
I have used this program and have modified it to do what I need it to do.
To test my program, I get a jpg image from my database as blob via odbc an then call compressJPEG (myBlobCopy) with the blob. To show what happend, i do some output in the classes. The size of the blob before an after calling the compressJPEG is the same.
Here the output:
126 23190 myimage.jpg Length of retrieved Blob: 4284416
Length of copy Blob: 4284416
Call compressJPEG (myBlobCopy)
now in compressJPEG
Back from compressJPEG: Length of retrieved Blob: 4284416
Here the class. It seems, that the compressed image (blob) is not returned. Please can you help me!!!!!!
import java.sql.*;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.sql.Blob;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
//import java.sql.SQLException;
class OracleConCompressBLOB{
public static void main(String args[]){
try{
//step1 load the driver class
Blob myBlob = null;
Blob myBlobCopy = null;
Class.forName("oracle.jdbc.driver.OracleDriver");
String dbURL = "jdbc:oracle:thin:#(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = xxxx)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED)(SERVICE_NAME = xxxx) ) )";
String strUserID = "xxxx";
String strPassword = "xxxxx";
//step2 create the connection object
Connection con=DriverManager.getConnection(dbURL,strUserID,strPassword);
//step3 create the statement object
Statement stmt=con.createStatement();
//step4 execute query
// fuer pbent ResultSet rs=stmt.executeQuery("select PRFO_ID, PRFO_PRAX_ID , PRFO_DATEINAME, PRFO_FOTO from T_PRAFOTO where PRFO_ID = 17");
ResultSet rs=stmt.executeQuery("select PRFO_ID, PRFO_PRAX_ID , PRFO_DATEINAME, PRFO_FOTO from T_PRAFOTO where PRFO_ID = 166 FOR UPDATE");
if (rs.next()) {
myBlob = rs.getBlob(4);
myBlobCopy = myBlob;
System.out.println(rs.getInt(1)+" "+rs.getInt(2)+" "+rs.getString(3)+" Length of retrieved Blob: " + myBlob.length());
System.out.println(" Length of copy Blob: " + myBlobCopy.length());
System.out.println("Call compressJPEG (myBlobCopy) ");
compressJPEG (myBlobCopy) ;
System.out.println("back from compressJPEG: Length of retrieved Blob: " + myBlobCopy.length());
}
//step5 close the connection object
con.close();
}catch(Exception e){ System.out.println(e);}
}
public static void compressJPEG(Blob blob) throws IOException {
// File imageFile = new File("myimage.jpg");
// File compressedImageFile = new File("myimage_compressed.jpg");
// InputStream is = new FileInputStream(imageFile);
// OutputStream os = new FileOutputStream(compressed ImageFile);
System.out.println("now in compressJPEG");
BufferedImage bufferedImage = null;
OutputStream outputStream = null;
float quality = 0.5f;
try {
// create a BufferedImage as the result of decoding the supplied InputStream
// BufferedImage image = ImageIO.read(is);
bufferedImage = ImageIO.read(blob.getBinaryStream());
outputStream = blob.setBinaryStream(0);
// test
// get all image writers for JPG format
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
if (!writers.hasNext())
throw new IllegalStateException("No writers found");
ImageWriter writer = (ImageWriter) writers.next();
ImageOutputStream ios = ImageIO.createImageOutputStream(outputStream);
writer.setOutput(ios);
ImageWriteParam param = writer.getDefaultWriteParam();
// compress to a given quality
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
// appends a complete image stream containing a single image and
//associated stream and image metadata and thumbnails to the output
writer.write(null, new IIOImage(bufferedImage, null, null), param);
// close all streams
// is.close();
// os.close();
// ios.close();
// writer.dispose();
outputStream.flush();
ios.close();
outputStream.close();
writer.dispose();
} catch (IOException e) {
e.printStackTrace();
}
catch (SQLException e) {
e.printStackTrace();
}
catch(IllegalArgumentException e) {
e.printStackTrace();
}
}
}
I need to copy a .jar file (which is a resource in my project) from a separate runnable jar to the startup folder in windows. Here's the code I have so far.
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Installer {
public static void main(String[] args) throws IOException
{
InputStream source = Installer.class.getResourceAsStream("prank.jar");
byte[] buffer = new byte[source.available()];
source.read(buffer);
File targetFile = new File(System.getProperty("user.home") + File.separator + "AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\prank.jar");
OutputStream outStream = new FileOutputStream(targetFile);
outStream.write(buffer);
outStream.close();
}
}
My problem is that after the jar file is copied, it is corrupt (although the size of the original and the copy are the same.) Any thoughts on how to do this and have a runnable jar at the end of the process?
Refer to InputStream#available does not work.
The following line
byte[] buffer = new byte[source.available()];
is not correct, as available only return estimate of the size, the jar will be corrupted when estimate is different with actual. (Example from Java – Write an InputStream to a File) seems incorrect as I can't find any reference that guarantee correctness of available for FileInputStream.
Solution from How to convert InputStream to File in Java is more robust,
private static void copyInputStreamToFile(InputStream inputStream, File file)
throws IOException {
try (FileOutputStream outputStream = new FileOutputStream(file)) {
int read;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
// commons-io
//IOUtils.copy(inputStream, outputStream);
}
}
You can consider to use
IOUtils#copy(InputStream, OutputStream)
Files#copy(InputStream, Path, CopyOption...) suggested by Holger for jdk 1.7 or above
Try this -
Path source = Paths.get("location1/abc.jar");
Path destination = Paths.get("location2/abc.jar");
try {
Files.copy(source, destination);
} catch(FileAlreadyExistsException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 7 years ago.
I am trying to write an buffered image into a file that appends the next buffered image bytes.I have the following code for which some runtime exception is thrown. when i run this code i get the following exception. Why and what has to be changed?
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
public class FileT
{
public static void main(String[] args)
{
try {
BufferedImage originalImage = ImageIO.read(new File("ani.jpg"));
int i=0,c=0;
// convert BufferedImage to byte array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(originalImage, "jpg", baos);
baos.flush();
byte[] imageInByte = baos.toByteArray();
byte[] copybuf = new byte[1024];
baos.close();
while(i<imageInByte.length)
{
copybuf[c]=imageInByte[i];
c++;
if(i%1023==0)
{
// convert byte array back to BufferedImage
InputStream in = new ByteArrayInputStream(copybuf);
BufferedImage bImageFromConvert = ImageIO.read(in);
ImageIO.write(bImageFromConvert, "jpg", new FileOutputStream(new File("ani1.jpg"),true));
}
copybuf = new byte[1024];
i++;
}
}
catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
Exception in thread "main" java.lang.IllegalArgumentException: image == null!
at javax.imageio.ImageTypeSpecifier.createFromRenderedImage(ImageTypeSpecifier.java:925)
at javax.imageio.ImageIO.getWriter(ImageIO.java:1592)
at javax.imageio.ImageIO.write(ImageIO.java:1578)
at FileT.main(FileT.java:45)
if(i%1023==0){
// convert byte array back to BufferedImage
InputStream in = new ByteArrayInputStream(copybuf);
BufferedImage bImageFromConvert = ImageIO.read(in);
ImageIO.write(bImageFromConvert, "jpg", new FileOutputStream(new File("ani1.jpg"),true));
}
copybuf = new byte[1024];
i++;
In this code you might want to change new FileOutputStream(new File()) to be casted to ImageOutputStream
It is very hard to determine from your question what you need, but I think this will fix it. If it doesn't just leave a comment and Ill try to fix it
Using the following libraries:
qrgen-1.2, zxing-core-1.7, and
zxing-j2se-1.7 I generated QRCode:
ByteArrayOutputStream out = QRCode.from(output.toString()).withSize(1000,1000).to(ImageType.PNG).stream();
FileOutputStream fout = new FileOutputStream(new File("D:\\QR_Code.JPG"));
fout.write(out.toByteArray());
fout.flush();
fout.close();
What I intend to do with it, is to send code to a method that accepts java.awt.Image.
How can I convert an instance of QRCode class into an instance of Image class without creating QRCode.JPG at the first place? As I see, this library doesn't provide users with methods that can carry this out, so is it possible at all? May be I can convert stream to Image?
Simply write the qr code to a byte array output stream then use byte array in the stream to create a BufferedImage.
import net.glxn.qrgen.QRCode;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
public static BufferedImage generate() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
QRCode.from("http://www.stackoverflow.com").writeTo(baos);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
return ImageIO.read(bais);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
I am able to convert my audio into byte values.
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
public class Audio_to_bytes {
public static void main(String args[]) throws IOException {
File WAV_FILE = new File("/home/cybersecurity/Desktop/scream2.wav");
ByteArrayOutputStream out = new ByteArrayOutputStream();
AudioInputStream in = null;
try {
in = AudioSystem.getAudioInputStream(WAV_FILE);
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
int read, i;
byte[] buff = new byte[1024];
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
out.flush();
byte[] audioBytes = out.toByteArray();
}
}
I want to identify audios which contains scream in them.For that i need to convert my audio into real numbers so that i can apply fft on it.Can anyone help me how this can be done
I came up with this code snippet and tested it. I hope it helps. I am allocating 4 floats (as bytes) I previously created and converted to bytes. Then I use the NIO FloatBuffer View of a ByteBuffer so NIO automatically returns 4 bytes as a float number without further treatment.
ByteBuffer bb = ByteBuffer.allocate(4*4);
bb.put(new byte[]{64,-112,0,0,66,-10, 22,-68, 66,9, 73, -43, 63,-114, 56, -38});
bb.rewind();
FloatBuffer floatBuffer = bb.asFloatBuffer();
for(int i = 0; i < 4;i++){
System.out.println(floatBuffer.get());
}