I’ve got a file with the following structure:
<number> <title> <text> <image data>
For example:
176 Elephant This image shows a cute elephant. <image data bytes go here>
I write the example above like this:
String filepath = "myfile.txt"; BufferedImage image = ...; //load some image here try(FileOutputStream output = new FileOutputStream(filepath); BufferedWriter writer = new BufferedWriter(new FileWriter(filepath))) { writer.write("Number: 176"); writer.newLine(); writer.write("Title: Elephant"); writer.newLine(); writer.write("Text: This image shows a cute elephant."); writer.newLine(); ImageIO.write(image, "png", output); writer.flush(); } catch (Exception) {//exception handling}
This works fine. However, I do not know how to read the file. The problem is that I need to call ImageIO.read() to parse the image, but I can’t call it with a BufferedReader. My draft looks like this:
String filepath = "myfile.txt"; try (BufferedReader reader = new BufferedReader(new java.io.FileReader(filepath))) { String number = reader.readLine(); reader.readLine(); //skip a free line String title = reader.readLine(); reader.readLine(); //skip a free line String text = reader.readLine(); reader.readLine(); //skip a free line BufferedImage image = ???; //how can I read the image here? <---------------- } catch (Exception e) {//error handling ...}
So: How can I read the image (using ImageIO)?
Any help is very appreciated 🙂
EDIT: Like I do the writing with two different Writers, is it possible to make use of two Readers while reading? Like “Read 3 lines of Strings, then read one line of binary data”.
Advertisement
Answer
The problem, as I can see it, is the mixing of the readers and streams..
Now, I tried using…
try (OutputStream output = new FileOutputStream(filepath); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output))) { writer.write("Number: 176"); writer.newLine(); writer.write("Title: Elephant"); writer.newLine(); writer.write("Text: This image shows a cute elephant."); writer.newLine(); writer.flush(); ImageIO.write(image, "png", output); output.flush(); } catch (Exception exp) { exp.printStackTrace(); }
To write the file. The hope was that the input stream position would be updated and the content would be written to the end of the file…
And
String filepath = "myfile.txt"; BufferedImage image = null; try (InputStream is = new FileInputStream(new File(filepath)); BufferedReader br = new BufferedReader(new InputStreamReader(is))) { String number = br.readLine(); String title = br.readLine(); String text = br.readLine(); System.out.println(number); System.out.println(title); System.out.println(text); ImageInputStream iis = ImageIO.createImageInputStream(is); image = ImageIO.read(iis); ImageIO.write(image, "png", new File("test.png")); } catch (Exception exp) { exp.printStackTrace(); }
To read it.
But this didn’t seem to work. What “might” be happening is the file position isn’t being updated to the correct position that we need it to read the image properly.
What I did instead was this…
import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import javax.imageio.ImageIO; import javax.imageio.stream.ImageInputStream; public class ReadWriteImage { public static void main(String[] args) { try { writeTest(); readTest(); } catch (IOException ex) { ex.printStackTrace(); } } public static void readTest() throws IOException { String filepath = "myfile.txt"; BufferedImage image = null; try (InputStream is = new FileInputStream(new File(filepath))) { int lineCount = 0; StringBuilder sb = new StringBuilder(128); String number = null; String title = null; String text = null; int b = -1; while (lineCount < 3 && (b = is.read()) != -1) { if ((char)b == 'n') { switch (lineCount) { case 0: number = sb.toString(); break; case 1: title = sb.toString(); break; case 2: text = sb.toString(); break; } sb.delete(0, sb.length()); lineCount++; } else { sb.append((char)b); } } System.out.println(number); System.out.println(title); System.out.println(text); ImageInputStream iis = ImageIO.createImageInputStream(is); image = ImageIO.read(iis); ImageIO.write(image, "png", new File("test.png")); } catch (Exception exp) { exp.printStackTrace(); } } public static void writeTest() throws IOException { String filepath = "myfile.txt"; BufferedImage image = ImageIO.read(new File("/Users/swhitehead/Dropbox/MegaTokyo/thumnails/2005-09-29-3957_400.jpg")); try (FileOutputStream output = new FileOutputStream(filepath)) { output.write("Number: 176n".getBytes()); output.write("Title: Elephantn".getBytes()); output.write("Text: This image shows a cute elephant.n".getBytes()); ImageIO.write(image, "png", output); output.flush(); } catch (Exception exp) { exp.printStackTrace(); } } }
Basically, I just used the OutputStream
and InputStream
directly…
Updated
The file contents starts with…
Number: 176 Title: Elephant Text: This image shows a cute elephant. âPNG
Welcome to 2021
So, the ordinal answer was made some time ago, the world has moved on. Unless you have a very specific need not to (ie transport speed/bandwidth restrictions), I would highly recommend making use of things like JSON and Base64 to send binary and text mixed together.
The main reason, it’s actually much simpler to encode/decode and maintain. The original solution is rather difficult to add new content to, as both ends need to be able to support the changes, where as something like JSON can (if done properly) get away with adding and removing content. It’s also easier to build much more complex parsing workflows, as you’re not reliant on the “read/write” position of the stream.
This example makes use of the org.json library, but you can use what ever library you want.
import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Base64; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JOptionPane; import org.json.JSONObject; public class Main { public static void main(String[] args) throws IOException { new Main(); } public Main() throws IOException { read(write()); } public String write() throws IOException { BufferedImage image = ImageIO.read(getClass().getResource("/images/Background.png")); JSONObject jobj = new JSONObject(); jobj.put("number", 176); jobj.put("title", "Elephant"); jobj.put("text", "This image shows a cute elepant"); try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { ImageIO.write(image, "png", baos); baos.flush(); byte[] encode = Base64.getEncoder().encode(baos.toByteArray()); String text = new String(encode, StandardCharsets.UTF_8); jobj.put("image", text); } String text = jobj.toString(); return text; } public void read(String jsonText) throws IOException { JSONObject jobj = new JSONObject(jsonText); System.out.println("Number = " + jobj.getInt("number")); System.out.println("Title = " + jobj.getString("title")); System.out.println("Text = " + jobj.getString("text")); String encodedImage = jobj.getString("image"); byte[] imageBytes = Base64.getDecoder().decode(encodedImage.getBytes(StandardCharsets.UTF_8)); try (ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes)) { BufferedImage image = ImageIO.read(bais); JLabel label = new JLabel(new ImageIcon(image)); label.setText("<html>Number = " + jobj.getInt("number") + "<br>Title = " + jobj.getString("title") + "<br>Text = " + jobj.getString("text")); JOptionPane.showMessageDialog(null, label); } } }
What if I want to send 2 files?
Then use a JSONArray
. org.json
is reasonably powerful, see Introduction to JSON-Java for more details. The Google implementation (GSON?) also provides a lot of functionality which can be used to encode/decode json from/to POJOs, if that’s your thing