Java clone jpg without losing quality of image - java

I have following method for coping jpg photos from one folder to another:
public static void copyImage(String from, String to) {
try {
File sourceimage = new File(from);
BufferedImage image = ImageIO.read(sourceimage);
ImageIO.write(image, "jpg", new File(to));
} catch (IOException ex) {
Logger.getLogger(ImgLib.class.getName()).log(Level.SEVERE, null, ex);
} catch (NullPointerException ex){
Logger.getLogger(ImgLib.class.getName()).log(Level.SEVERE, null, ex);
}
}
It works, but little bit loosing quality of photo.
How I can achieve "perfect" cloning without loosing quality?

Yes, you are right. In this line:
ImageIO.write(image, "jpg", new File(to));
Your method is still re-encoding the image data which, with a lossy format like JPEG, will inevitably cause a loss of fidelity in the image.
I think, you may try to copy the image file using this code:
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(new File("path/to/img/src"));
os = new FileOutputStream(new File("path/to/img/dest"));
byte[] buffer = new byte[8192];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
} finally {
is.close();
os.close();
}
Also, you may Apache Commons IOUtils to simplify copying from one stream to the other or if you are using Java 8 then you can just call Files.copy method.

InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(new File("path/to/img/src"));
os = new FileOutputStream(new File("path/to/img/dest"));
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
} finally {
is.close();
os.close();
}

You have used BufferedImage which read the file into a image object.
instead you should read and write image file in the same way as you do with binary files (use InputStraem and OutputStream).

Related

Why I'm getting 2 different base64 encoded string from the same image (Android Studio and Netbeans)?

I need to convert an image choosen from the gallery into a base64 string.
Then, I pass the baase64 string as a parameter for an API request.
There is only one problem. When I use netbeans it works, when I use Android Studio it doesn't. I found that the problem is the base64 string output. I don't know why, if I use the same exactly image, the output is different.
Maybe the problem happpens because I have to use the same exact method to read the image file...?
That's my code in Netbeans(working):
InputStream inputStream = new FileInputStream("testImage.jpg");
byte[] bytes;
byte[] buffer = new byte[8192];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
while ((bytesRead = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
bytes = output.toByteArray();
String encodedFile = Base64.getEncoder().encodeToString(bytes);
And that's the code in Android Studio:
Bitmap bitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] imageData = baos.toByteArray();
InputStream inputStream = getContentResolver().openInputStream(imageUri);
byte[] buffer_new = new byte[8192];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
while ((bytesRead = inputStream.read(buffer_new)) != -1) {
output.write(buffer_new, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
bytes = output.toByteArray();
String encodedImage = Base64.encodeToString(bytes,Base64.DEFAULT);
Log.v("encodedImage", encodedImage);
The ouput string are almost the same only at the first char..then they are different..with the android studio encoded string, I get this error when I try to use the API.
BAD_ARGUMENTS:<key>
Error while parsing some arguments. This error may be caused by illegal type or length of argument.
What should I use to get the same base64 string?
ps. in Netbeans the image is a file in the same folder of the project, in android studio the user can load a picture from gallery.

How can I convert BufferedImage to byte[]?

public void actionPerformed(java.awt.event.ActionEvent evt) {
Connection cn = null;
Object source = evt.getSource();
JFileChooser filechooser= new JFileChooser();
filechooser.setDialogTitle("Choose Your File");
filechooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
int returnval=filechooser.showOpenDialog(this);
if(returnval==JFileChooser.APPROVE_OPTION)
{
File file = filechooser.getSelectedFile();
BufferedImage bi;
try
{
bi=ImageIO.read(file);
lbl_movieCover.setIcon(new ImageIcon(bi));
}
catch(IOException e)
{
}
//this.pack();
}
Above is my code for choosing the image and displaying the image to JLabel. My problem is that, I don't know how to convert it to byte[] so I could save it to my database. By the way, I'm using MySQL for my database. If you guys know how to do it, please let me know.
Use ImageIO.write to write the image through a ByteArrayOutputStream, for example
ByteArrayOutputStream baos = null;
try {
baos = new ByteArrayOutputStream();
ImageIO.write(bi, "png", baos);
} finally {
try {
baos.close();
} catch (Exception e) {
}
}
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
You can then use the resulting byte[] array or ByteArrayInputStream and pass this to the setBlob method of a PreparedStatement
You could use a ByteArrayOutputStream and ImageIO to write an image to a byte array, like this:
static byte[] imageToByteArray(BufferedImage image) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
ImageIO.write(image, "png", stream);
} catch(IOException e) {
// This *shouldn't* happen with a ByteArrayOutputStream, but if it
// somehow does happen, then we don't want to just ignore it
throw new RuntimeException(e);
}
return stream.toByteArray();
// ByteArrayOutputStreams don't need to be closed (the documentation says so)
}
You can use this method-
/**
* #param userSpaceImage
* #return byte array of supplied image
*/
public byte[] getByteData(BufferedImage userSpaceImage) {
WritableRaster raster = userSpaceImage.getRaster();
DataBufferByte buffer = (DataBufferByte) raster.getDataBuffer();
return buffer.getData();
}
Use it like-
//file from filechooser
BufferedImage originalImage = ImageIO.read(file);
byte image[] = getByteData(originalImage);
Note that if image type is that of int e.g. BufferedImage.TYPE_INT_RGB then
you will get cast exception. Following method can be used to convert to suitable type-
/**
* this method convert supplied image to suitable type
* it is needed because we need bytes of array so TYPE_INT images must be
* converted to BYTE_BGR or so
* #param originalImage loaded from file-chooser
* #return
*/
public BufferedImage convertImage(BufferedImage originalImage) {
int newImageType = originalImage.getType();
/**
* Converting int to byte since byte array is needed later to modify
* the image
*/
if (newImageType == BufferedImage.TYPE_INT_RGB
|| newImageType == BufferedImage.TYPE_INT_BGR) {
newImageType = BufferedImage.TYPE_3BYTE_BGR;
} else if (newImageType == BufferedImage.TYPE_INT_ARGB) {
newImageType = BufferedImage.TYPE_4BYTE_ABGR;
} else if (newImageType == BufferedImage.TYPE_INT_ARGB_PRE) {
newImageType = BufferedImage.TYPE_4BYTE_ABGR_PRE;
}
BufferedImage newImage = new BufferedImage(originalImage.getWidth(),
originalImage.getHeight(), newImageType);
Graphics g = newImage.getGraphics();
g.drawImage(originalImage, 0, 0, null);
g.dispose();
return newImage;
}
While both #MadProgrammer's and #immibis' answers are technically correct and answers your question, you don't really want to copy an image file by decoding it, and re-encoding it. This is because it's slower, but more importantly, you will lose quality for certain image formats (most notably JPEG) and any metadata associated with the image will be lost (this last part could of course be intentional, but there are better ways to do this without ruining the image quality).
So, instead, do as #immibis seems to hint at in his comment, just open a FileInputStream and read the bytes directly from the file, and into the database. You should be able to open an OutputStream to the blob in your database as well, so you can save some memory (a good thing, if your files are large) by not reading the entire file contents into a byte array, before writing.
Something like:
File file = filechooser.getSelectedFile();
// ... show image in label as before (but please, handle the IOException)
InputStream input = new FileInputStream(file);
try {
Blob blob = ...; // Get from JDBC connection
OutputStream output = blob.setBinaryStream(0);
try {
FileUtils.copy(input, output);
}
finally {
output.close();
}
}
finally {
input.close();
}
FileUtils.copy can be implemented as:
public void copy(final InputStream in, final OutputStream out) {
byte[] buffer = new byte[1024];
int count;
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
// Flush out stream, to write any remaining buffered data
out.flush();
}

How to convert loaded bytes into image without ImageIO.read()

I can't use ImageIO.read() because of my own restrictions. I can only load bytes after GET request and I need to save this bytes to file as image. But it seems to me, that there also loads some extra data, which browser usually filter (maybe response headers). So I get the array of raw bytes which I even can't open as image.
What should I do with this bytes?
Example:
byte[] buf = ContentLoader.loadBytes(new URL("http://images.visitcanberra.com.au/images/canberra_hero_image.jpg"));
try {
FileOutputStream fileOutputStream = new FileOutputStream(new File("D:\\image.jpg"));
fileOutputStream.write(buf);
fileOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
loadBytes() method:
public static byte[] loadBytes(URL url) {
ByteArrayOutputStream boutArray = new ByteArrayOutputStream();
try {
URLConnection connection = url.openConnection();
BufferedInputStream bin = new BufferedInputStream(connection.getInputStream());
byte[] buffer = new byte[1024 * 16];
while (bin.read(buffer) != -1) {
boutArray.write(buffer);
boutArray.flush();
}
bin.close();
} catch (Exception e) {
return null;
}
return boutArray.toByteArray();
}
Usual problems. The standard way to copy a stream in Java is:
int count;
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
out.close();
in.close();
Note that you need to store the result returned by read() into a variable; that you need to use it in the next write() call; that you shouldn't flush() inside a loop; and that you need to close the input and output streams.
And why you're using a ByteArrayInputStream at all is a mystery. It's just a waste of time and space. Read directly from the URL input stream, and write directly to the FileOutputStream.
The following code works for me:-
URL url = new URL("my url...");
InputStream is = url.openStream();
OutputStream os = new FileOutputStream("img.jpg");
byte[] b = new byte[2048];
int length;
while ((length = is.read(b)) != -1) {
os.write(b, 0, length);
}
is.close();
os.close();

OutOfMemory when creating Base64 string in java?

I used ostermillerutils library to create base64 string but I get OutOfMemory error if the image is heavy. If the image I try to convert is a simple image, the code is working fine.
public String createBase64String(InputStream in) {
//collect = new ByteArrayOutputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
try {
for(int readNum; (readNum = in.read(buf)) != -1; ) {
bos.write(buf, 0, readNum);
}
}
catch (IOException ex) {
Logger.getInstance().debug("XML createBase64String: IOException");
return null;
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
;
}
}
}
byte[] ba = bos.toByteArray();
String coded = Base64.encodeToString(ba);
return coded;
}
I also tried doing this but the base64 was incorrect when I tried to decode it.
public void createBase64String(InputStream in) throws IOException {
//collect = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int readNum = 0;
try {
while((readNum = in.read(buf)) != -1)
{
smtp.addBase64(Base64.encodeBase64String(buf));
}
}
catch (IOException ex) {
Logger.getInstance().debug("XML createBase64String: IOException");
}
finally {
if (in != null) {
in.close();
}
}
}
Please suggest solutions for JDK 1.4 and also for later versions of Java.
If you like to write the encoded content straight into a file then use the following code
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();
}
IOUtils class from Apache Commons IO.
EDIT
Since you want to do it using BufferedWriter, use it as follows
OutputStream out = Base64OutputStream(smtpSocket.getOutputStream());
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
IOUtils.copy(is, bw);
It sounds like the problem is that you're having to manipulate too much data in memory when you read the entire image. One fix would be to increase the Java heap size until you have enough memory, but that would just be avoiding the problem rather than solving it.
A better option would be to look at a streaming implementation of a Base64 encoder. This would mean you're only working on a subset of the image at any time. I believe that Base64OutputStream from Apache Commons would do the job for you.
I've fixed my problem by using javabase64-1.3.1.jar library.
OutputStream fos2 = FileUtil.getOutputStream(base64FileName, FileUtil.HDD);
InputStream in2 = FileUtil.getInputStream(fileName, FileUtil.HDD);
Base64.encode(in2, fos2);
in2.close();
fos2.close();
I stored the base64 string to a text file first.
public void createBase64String(InputStream in) throws IOException {
baos = new ByteArrayOutputStream();
byte[] buf = new byte[BUFFER_SIZE];
int readNum = 0;
smtp.addBase64("\t\t");
try {
while ((readNum = in.read(buf)) >= 0) {
baos.write(buf, 0, readNum);
smtp.addBase64(baos.toString());
baos.reset();
}
}
catch (IOException ex) {
LogUtil.error("Sending of Base64 String to SMTP: IOException: " + ex);
}
finally {
if (in != null) {
in.close();
baos.close();
}
}
baos = null;
buf = null;
}
then send each line to smtp's socket outputstream.
From Java 8 onwards, there is a simple way to implement base64 encoding in an output stream with one line of code and no external dependencies:
import java.util.Base64;
OutputStream os = ...
OutputStream base64 = Base64.getEncoder().wrap(os);
Base64 also provides other flavors of base64 encoder; see javadocs:
Base64
Base64.Encoder.wrap

Line by line image swing

I want to show an image as it is downloading, I have the URL, and I am trying to get the image parts like this:
InputStream openStream = url.openStream();
DataInputStream dis = new DataInputStream(new BufferedInputStream(openStream));
ByteArrayOutputStream os = new ByteArrayOutputStream();
while ((s = dis.read(b)) != -1) {
os.write(b , 0, s);
support.firePropertyChange("stream", null, os);
}
This way, any listener get the stream and creates an image, this way:
if("stream".equals(evt.getPropertyName())){
try {
ByteArrayOutputStream stream = (ByteArrayOutputStream) evt.getNewValue();
byte[] byteArray = stream.toByteArray();
stream.flush();
Image createImage = Toolkit.getDefaultToolkit().createImage(byteArray);
this.getContentPane().add(new JLabel(new ImageIcon(createImage)));
} catch (IOException ex) {
Logger.getLogger(ImageTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
However, I am getting a "Premature end of JPEG file sun.awt.image.ImageFormatException: JPEG datastream contains no image" error, the image is a JPG image format, is there any library or method known to make something similar?
It would depend on the image decoder, but you could try ImageObserver. There's an example in Tracking Image Loading: MediaTracker and ImageObserver.

Categories

Resources