I write images and other data to binary file. When I read image via ImageIO.read(InputStream) from that file, it reads image, it is ok, but method closes given input stream and I cant proceed to read other data.
Why so it is made?
Then how read image without closing stream?
EDIT: It is simple code that writes image and string after into file:
File f = new File("test.bin");
if(f.exists())
f.delete();
f.createNewFile();
DataOutputStream os = new DataOutputStream(new FileOutputStream(f));
BufferedImage img = ImageIO.read(new File("test.jpg"));
ImageIO.write(img, "jpg", os);
os.writeUTF("test string after image");
os.close();
And code that reads all:
DataInputStream is = new DataInputStream(new FileInputStream(f));
BufferedImage img = ImageIO.read(is);
String s = is.readUTF(); // on this line EOFException occurs
System.out.println(s);
NetBeans output:
Exception in thread "main" java.io.EOFException
at java.io.DataInputStream.readUnsignedShort(DataInputStream.java:340)
at java.io.DataInputStream.readUTF(DataInputStream.java:589)
at java.io.DataInputStream.readUTF(DataInputStream.java:564)
at mediamanager.Main.test(Main.java:105)
at mediamanager.Main.main(Main.java:44)
May be I'm doing something wrong?
Quote from the documentation of ImageIO.read(InputStream)
This method does not close the provided InputStream after the read operation has completed; it is the responsibility of the caller to close the stream, if desired.
Emphasis not mine.
The problem is elsewhere. Probably in your code.
I can see two possible causes of such behaviour:
Image reader use buffer to read data from the stream to improve performance. So it reads more data from the stream.
Also image reader could try to read EXIF for already parsed image. Such information usually appended at the end of file to avoid full file rewriting when you are just adding a couple of piece of information about the image.
Try ImageIO.setUseCash(false) it could help.
Related
I'm creating a Chat in java for a university project, and one of the requirements is each user must have an image associate, this can be done through registration windows and data modification windows, in registration everything works great, but on the modification window, the program throws an exception when i try to send big files, both codes (registration and modification) are basiccally the same, changing only variables and path, but still gives my problem only in modification
Here is my code:
Client:
BufferedImage image = ImageIO.read(new File(usuario.getImagen().getCanonicalPath()));
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", byteArrayOutputStream);
byte[] size = ByteBuffer.allocate(4).putInt(byteArrayOutputStream.size()).array();
salida.write(size);
salida.write(byteArrayOutputStream.toByteArray());
salida.flush();
Server:
dir = new File ("." + "/Documentos/Imagenes de Verificacion/" +
usuarioRegistro.getNombreDeUsuario() + ".jpg");
sizeAr = new byte[4];
entrada.read(sizeAr);
size = ByteBuffer.wrap(sizeAr).asIntBuffer().get();
imageAr = new byte[size];
entrada.readFully(imageAr);
image = ImageIO.read(new ByteArrayInputStream(imageAr));
ImageIO.write(image, "jpg", new File(dir.getCanonicalPath()));
usuarioRegistro.setImagen(dir.getCanonicalFile());
And the exception is:
Exception in thread "Thread-0" java.lang.IllegalArgumentException: image == null!
at javax.imageio.ImageTypeSpecifier.createFromRenderedImage(ImageTypeSpecifier.java:925)
at javax.imageio.ImageIO.getWriter(ImageIO.java:1591)
at javax.imageio.ImageIO.write(ImageIO.java:1520)
at com.ucab.javachat.Servidor.model.ServidorModel.run(ServidorModel.java:198)
The line ServiorModel.java:198 is: ImageIO.write(image, "jpg", new File(dir.getCanonicalPath()));
In my tests i can send images of 20, 30, 80, 200 Kb, but when i try to send the 2.1mb file gives the error.
I think this i related with some data loose on the byteArray (maybe header data?) but what i dont know is how to fix it, my register window method uses the same sockets and OutputStream to send data and i succesfully send a 24mb image.
As per the documentation:
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. The current cache settings from getUseCacheand
getCacheDirectory will be used to control caching in the
ImageInputStream that is created.
Note that there is no read method that takes a filename as a String;
use this method instead after creating a File from the filename.
This method does not attempt to locate ImageReaders that can read
directly from a File; that may be accomplished using IIORegistry and
ImageReaderSpi.
Make sure you register an ImageReader or wrap your file on a FileInputStream, but since your implementation works I bet it's the image causing issues therefore,
Make sure that your image is of type: GIF, PNG, JPEG, BMP, and WBMP for these are the types supported by the class.
I know how to create / write/ close buffered output stream. How do I reopen the stream and append data to end of the file.
You can't reopen a stream... but you can create a new stream which will append:
FileOutputStream output = new FileOutputStream(file, true);
See the constructor list for FileOutputStream for the various options.
(Of course, you can then wrap that FileOutputStream with a BufferedOutputStream.)
I have an app that creates multiple files using a byte array it gets from a Socket InputStream. The file saves perfectly when I just save one file, but if I save the one file then re-instantiate the file stream and save a different file, the first file gets corrupted and the second file is saved perfectly. I opened the two files in a text editor and it seems (about...)the first 1/5th of the first file is blank spaces but the second file is full, and they both have the same size properties(9,128,731 bytes). The following example is a duplication of the senario but with the same corruption result:
FileOutputStream outStream;
outStream = new FileOutputStream("/mnt/sdcard/testmp3.mp3");
File file = new File("/mnt/sdcard/test.mp3");
FileInputStream inStream = new FileInputStream(file);
byte[] buffer = new byte[9128731];
inStream.read(buffer);
outStream.write(buffer, 0, buffer.length);
inStream.close();
outStream.flush();
outStream.close();
outStream = null;
outStream = new FileOutputStream("/mnt/sdcard/testmp32.mp3");
outStream.write(buffer, 0, buffer.length);
inStream.close();
outStream.flush();
outStream.close();
outStream = null;
I tried this EXACT code in a regular java application and both files were saved without a problem. Does anyone know why the android is doing this?
Any help would be GREATLY appreciated
As jtahlborn mentioned you cannot assume that InputStream.read(byte[]) will always read as many bytes as you want. As well you should avoid using such a large byte array to write out at once. At least not without buffering, you could potentially overflow something. You can handle these concerns and save some memory by copying the file like this:
File inFile = new File("/mnt/sdcard/test.mp3");
File outFile = new File("/mnt/sdcard/testmp3.mp3");
FileInputStream inStream = new FileInputStream(inFile);
FileOutputStream outStream = new FileOutputStream(outFile);
byte[] buffer = new byte[65536];
int len;
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
inStream.close();
outStream.close();
I see some potential issues that can get you started debugging:
You writing to the first output stream before you close the input stream. This is a bit weird.
You can't accurately gauge the similarity/difference between two binary files using a text editor. You need to look at the files in a hex editor (or better, Audacity)
I would use BufferedOutputStream as suggested by the Android docs:
out = new BufferedOutputStream(new FileOutputStream(file));
http://developer.android.com/reference/java/io/FileOutputStream.html
As a debugging technique, print the contents of buffer after the first write. Also, inStream.read() returns an int. I would additionally compare this to buffer.length and make sure they are the same. Regardless, I would just call write(buffer) instead of write(buffer, 0, buffer.length) unless you have a really good reason.
-tjw
You are assuming that the read() call will read as many bytes as you want. that is incorrect. that method is free to read anywhere from 1 to buffer.length bytes. that is why you should always use the return value to determine how many bytes were actually read. there are plenty of streams tutorials out there which will show you how to correctly read from a java stream (i.e. how to completely fill your buffer).
If anyone's having the same problem and wondering how o fix it I found out the problem was being caused by my SD card. I bought a 32gb kingston sd card and just yesterday I decided to try running the same code again accept using the internal storage instead and everything worked perfectly. I also tried the stock 2gb SD card it came with and it also worked perfectly. I glad to know my code works great but a little frustrated I spent 50 bucks on a defective memory card. Thanks for everyones input.
For an applet I'm working on I need to convert a BufferedImage file to an input stream so that I can upload the image to my MySQL server. Originally I was using this code:
Class.forName("com.mysql.jdbc.Driver").newInstance();
Connection connection =
DriverManager.getConnection(connectionURL, "user", "pass");
psmnt = connection.prepareStatement(
"insert into save_image(user, image) values(?,?)");
psmnt.setString(1, username);
ImageIO.write(image, "png", new File("C://image.png"));
File imageFile = new File("C://image.png");
FileInputStream fis = new FileInputStream(imageFile);
psmnt.setBinaryStream(2, (InputStream)fis, (fis.length()));
int s = psmnt.executeUpdate();
if(s > 0) {
System.out.println("done");
}
(while catching the relevant exceptions) The code hangs on the part where the applet attempts to save the image to the computer. The code worked perfectly in Eclipse or whenever I ran the applet from the localhost, so I'm assuming the problem is in the privileges that the applet has in saving files to the user's computer.
I was just was wondering if there was a way to turn the image file into an inputstream without having to save a file to the user's computer. I tried using:
ImageIO.createImageInputStream(image);
But then I couldn't convert the ImageInputStream back to an InputStream. Any Suggestions?
Thanks!
Typically you would use a ByteArrayOutputStream for that purpose. It acts as an in-memory stream.
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image,"png", os);
InputStream fis = new ByteArrayInputStream(os.toByteArray());
Have you tried writing to a ByteArrayOutputStream and then creating a ByteArrayInputStream from that data to read from? (Call toArray on the ByteArrayOutputStream and then call the constructor of ByteArrayInputStream which will wrap that byte array.)
Be careful using BytArray streams: if the image is large, that code will fail. i have not done much applet coding, but it's possible that the temp dir is available for writing (e.g. File.createTempFile() ).
I have a database file in res/raw/ folder. I am calling Resources.openRawResource() with the file name as R.raw.FileName and I get an input stream, but I have an another database file in device, so to copy the contents of that db to the device db I use:
BufferedInputStream bi = new BufferedInputStream(is);
and FileOutputStream, but I get an exception that database file is corrupted. How can I proceed?
I try to read the file using File and FileInputStream and the path as /res/raw/fileName, but that also doesn't work.
Yes, you should be able to use openRawResource to copy a binary across from your raw resource folder to the device.
Based on the example code in the API demos (content/ReadAsset), you should be able to use a variation of the following code snippet to read the db file data.
InputStream ins = getResources().openRawResource(R.raw.my_db_file);
ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
int size = 0;
// Read the entire resource into a local byte buffer.
byte[] buffer = new byte[1024];
while((size=ins.read(buffer,0,1024))>=0){
outputStream.write(buffer,0,size);
}
ins.close();
buffer=outputStream.toByteArray();
A copy of your file should now exist in buffer, so you can use a FileOutputStream to save the buffer to a new file.
FileOutputStream fos = new FileOutputStream("mycopy.db");
fos.write(buffer);
fos.close();
InputStream.available has severe limitations and should never be used to determine the length of the content available for streaming.
http://developer.android.com/reference/java/io/FileInputStream.html#available():
"[...]Returns an estimated number of bytes that can be read or skipped without blocking for more input. [...]Note that this method provides such a weak guarantee that it is not very useful in practice."
You have 3 solutions:
Go through the content twice, first just to compute content length, second to actually read the data
Since Android resources are prepared by you, the developer, hardcode its expected length
Put the file in the /asset directory and read it through AssetManager which gives you access to AssetFileDescriptor and its content length methods. This may however give you the UNKNOWN value for length, which isn't that useful.