In my API (Spring boot) I have an endpoint where users can upload multiple file at once. The endpoint takes as input a list of MultipartFile.
I wish not to directly pass this MultipartFile object to the service directly so I loop through each MultipartFile and create a simple map that stored the filename and its InputStream.
Like this:
for (MultipartFile file : files) {
try (InputStream is = multipartFile.getInputStream()) {
filesMap.put(file.getOriginalFilename(), is);
}
}
service.uploadFiles(filesMap)
My understanding for Java streams and streams closing is quite limited.
I thought that try-with-resources automatically closes the InputStream once the code reached the end of the try block.
In the above code when does exactly the the multipartFile.getInputStream() gets closed?
The fact that I'm storing the stream in a map will that cause a memory leak?
Stream closes right after execution reaches closing bracket of try block.
It is okay to store InputStream anywhere after you closed it.
But be aware of that you can't read anything from this stream after you closes it.
Thanks to comments
Also, be aware of that some streams have special behavior on close() and it always depends on Stream realization.
For example:
If you try to read from closed FileInputStream you will get
java.io.IOException: Stream Closed
If you try to read from closed ByteArrayInputStream it will be okay, because of it's special close() realization: public void close() throws IOException {}
When does exactly the multipartFile.getInputStream() gets closed?
try (InputStream is = multipartFile.getInputStream()) {
filesMap.put(file.getOriginalFilename(), is);
} // <-- here
The try-with-resources statement ensures that each resource is closed at the end of the statement.
The fact that I'm storing the stream in a map will that cause a memory leak?
No, your collection just keeps closed InputStreams and you won't be able to read from them (in addition, you will get IOException).
Related
I saw this example, and I didn't see the close() method invoked on the InputStream, so would prop.load() close the stream automatically? Or is there a bug in the example?
The Stream is not closed after Properties.load ()
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream(new File("abc.properties"));
new Properties().load(in);
System.out.println(in.read());
}
The above code returns "-1" so the stream is not closed. Otherwise it should have thrown java.io.IOException: Stream Closed
Why do you ask when the javadoc of Properties.load(InputStream inStream) says this?
The specified stream remains open after this method returns.
It has been saying that since Java 6.
As EJP said in a comment: Don't rely on arbitrary Internet junk. Use the official Oracle Java documentation as your primary source of information.
The following try-with-resources will close the InputStream automatically (you can add catch and finally if needed):
try (InputStream is = new FileInputStream("properties.txt")) {
// is will be closed automatically
}
Any resource declared within a try block opening will be closed. Hence, the new construct shields you from having to pair try blocks with corresponding finally blocks that are dedicated to proper resource management.
Article by Oracle here: http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html.
Consider the following code snippet getInputStreamForRead() method creates and returns a new input stream for read.
InputStream is = getInputStreamForRead(); //This method creates and returns an input stream for file read
is = getDecompressedStream(is);
Since the orginal file content is compressed and stored it has to be decompressed while reading. Hence getDecompressedStream() method below would provide option to decompress the stream content
public InputStream getDecompressedStream(InputStream is) throws Exception {
return new GZIPInputStream(is);
}
Have the following doubts
Which one is correct for the above snippet
is = getDecompressedStream(is)
or
InputStream newStream = getDecompressedStream(is)
Will reusing the InputStream variable again cause any trouble?
I'm completely new with streams. Kindly help me to know about this.
As long as:
you're not manipulating the original InputStream between the original assignment and the new invocation
you're always closing your streams in a finally statement
... you should be fine re-assigning to the original variable - it's just a new value passed to an existing reference.
In fact, that may be the recommended way, since you get to only close one Closeable programmatically, as GZIPInputStream#close...
Closes this input stream and releases any system resources associated with the stream.
(see here - I read this as, "closes the underlying stream").
Since you want to close the input stream correctly, the best way is to create the input stream using chaining, and using a try-with-resources to handle the close for you.
try (InputStream is = getDecompressedStream(getInputStreamForRead())) {
// code using stream here
}
I usually open files by fetching a BufferedReader:
Files.newBufferedReader(myPath).lines()
.doStuff()
.doMoreStuff();
What happens when the method throws an Exception, will the Reader be closed automatically like with a try-with-resource? I found no reference in the docs.
The Files.newBufferedReader is just a utility/factory method to create a BufferedReader for the File. Internally it eventually does a new BufferedReader() to create the BufferedReader.
So, you would have to treat it the same way as you would create the BufferedReader using the new operator. If you want it to be auto-closed at the end of the try block, you would have to use the try-with-resource as you would, if you had to create it using the new operator.
This question already has answers here:
Java multiple file transfer over socket
(3 answers)
Closed 8 years ago.
I'm trying to create a simple client where at first I comunicate to a server:
A filename
The sequence of chunks which compose the file
So for the first one I thought to use to a BufferedWriter: this choice was made since I can't use on the server a InputStreamReader from the moment that the readLine() method is deprecated. However, for the second one I used a OutputStreamWriter since it is the better (only?) one choice to write a byte array on a socket.
So, this is the first version of my client code:
public class Client
{
private static final int PART_SIZE = 1000000; // 1MB
public static void main(String[] args) throws IOException
{
final Path file = Paths.get(args[0]);
final String filenameBase = file.getFileName().toString();
final byte[] buf = new byte[PART_SIZE];
Socket socket = new Socket(InetAddress.getLocalHost(),8080);
System.out.println("Socket created");
int partNumber = 0;
Path part;
int bytesRead;
byte[] toWrite;
try (
final InputStream in = Files.newInputStream(file);
final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
final DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
) {
System.out.println("closed="+socket.isClosed());
bw.write(filenameBase,0,filenameBase.length());
//other stuff for the chunk creation and spedition
}
}
}
However, if I run this code, this exception occours:
Exception in thread "main" java.net.SocketException: Socket closed
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:121)
at java.net.SocketOutputStream.write(SocketOutputStream.java:159)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
at sun.nio.cs.StreamEncoder.implClose(StreamEncoder.java:316)
at sun.nio.cs.StreamEncoder.close(StreamEncoder.java:149)
at java.io.OutputStreamWriter.close(OutputStreamWriter.java:233)
at java.io.BufferedWriter.close(BufferedWriter.java:266)
at PAD.Charlie.Client.App.main(App.java:50)
The strange thing is that if I change the order between the BufferedWriter and the DataOutputStream inside the try everthing works fine!
Actually the idea has come because I remembered something about it from the java course, but I can't really remember the details! Can you help me about this doubt that I have? Thanks a lot! :)
First of all, what you are doing is borderline crazy. You appear to be intending to write both text and binary data the same stream:
It is going to be difficult to control the interleaving of the two kinds of data since you are using a buffered writer at that point in the stack.
Even if you get the interleaving right, the "other end" has the problem of unpicking it to separate the text and binary.
You attempt to justify your decision use two stream stacks on the output stream as follows:
So for the first one I thought to use to a BufferedWriter: this choice was made since I can't use on the server a InputStreamReader from the moment that the readLine() method is deprecated. However, for the second one I used a OutputStreamWriter since it is the better (only?) one choice to write a byte array on a socket.
I don't follow your logic. But the fact that one approach doesn't work does not necessarily mean that (any) other one will.
If you want a solution that will work, then I can think of a few. The simplest is to use DataOutputStream only on the client side, and use writeUTF to write the file name and writeInt + write to write the chunks. Indicate the end of file by sending a chunk size of zero.
(You could also send the file as one big chunk if you know beforehand how many bytes you will be sending.)
The server-side code should mirror the client-side in its calls on a DataInputStream.
But the reason for difference in behaviour that you are seeing is that the order of the declarations in the try initializations determines the order in which the streams are closed at the end of the try block.
If the writer is closed first then:
BufferedWriter.close()
-> BufferedWriter.flush() -> OutputStreamWriter.write()
-> OutputStreamWriter.close() -> SocketOutputStream.close()
DataOutputStream.close() -> SocketOutputStream.close()
This is OK because the second set of closes does not need to write any data.
If the writer is closed second then:
DataOutputStream.close() -> SocketOutputStream.close()
BufferedWriter.close()
-> BufferedWriter.flush() -> OutputStreamWriter.write() // FAIL
The failure happens because the flush cannot write data to the socket because you have already (implicitly) closed it.
Because closing the BufferedWriter flushes it, and if you create the writer first it will be closed last, after the stream, and closing either of them closes the socket. See the stack trace. DataOutputStream isn't buffered, so flushing it does nothing.
NB:
... since I can't use on the server a InputStreamReader from the moment that the readLine() method is deprecated. However, for the second one I used a OutputStreamWriter since it is the better (only?) one choice to write a byte array on a socket.
None of this makes sense. InputStreamReader doesn't have a readLine() method, let alone one that is deprecated; and OutputStreamWriter writes chars, not bytes.
Today, when I was working on some kind of servlet which was writing some information to some file present on my hard disk, I was using the following code to perform the write operation
File f=new File("c:/users/dell/desktop/ja/MyLOgs.txt");
PrintWriter out=new PrintWriter(new FileWriter(f,true));
out.println("the name of the user is "+name+"\n");
out.println("the email of the user is "+ email+"\n");
out.close(); //**my question is about this statement**
When I was not using the statement, the servlet was compiling well, but it was not writing anything to the file, but when I included it, then the write operation was successfully performed. My questions are:
Why was the data not being written to the file when I was not including that statement (even my servlet was compiling without any errors)?
Up to which extent the close operation is considerable for the streams?
Calling close() causes all the data to be flushed. You have constructed a PrintWriter without enabling auto-flush (a second argument to one of the constructors), which would mean you would have to manually call flush(), which close() does for you.
Closing also frees up any system resources used by having the file open. Although the VM and Operating System will eventually close the file, it is good practice to close it when you are finished with it to save memory on the computer.
You may also which to put the close() inside a finally block to ensure it always gets called. Such as:
PrintWriter out = null;
try {
File f = new File("c:/users/dell/desktop/ja/MyLOgs.txt");
out = new PrintWriter(new FileWriter(f,true));
out.println("the name of the user is "+name+"\n");
out.println("the email of the user is "+ email+"\n");
} finally {
out.close();
}
See: PrintWriter
Sanchit also makes a good point about getting the Java 7 VM to automatically close your streams the moment you don't need them automatically.
When you close a PrintWriter, it will flush all of its data out to wherever you want the data to go. It doesn't automatically do this because if it did every time you wrote to something, it would be very inefficient as writing is not an easy process.
You could achieve the same effect with flush();, but you should always close streams - see here: http://www.javapractices.com/topic/TopicAction.do?Id=8 and here: http://docs.oracle.com/javase/tutorial/jndi/ldap/close.html. Always call close(); on streams when you are done using them. Additionally, to make sure it is always closed regardless of exceptions, you could do this:
try {
//do stuff
} finally {
outputStream.close():
}
It is because the PrintWriter buffers your data in order for not making I/O operations repeatedly for every write operation (which is very expensive). When you call close() the Buffer flushes into the file. You can also call flush() for forcing the data to be written without closing the stream.
Streams automatically flush their data before closing. So you can either manually flush the data every once in a while using out.flush(); or you can just close the stream once you are done with it. When the program ends, streams close and your data gets flushed, this is why most of the time people do not close their streams!
Using Java 7 you can do something like this below which will auto close your streams in the order you open them.
public static void main(String[] args) {
String name = "";
String email = "";
File f = new File("c:/users/dell/desktop/ja/MyLOgs.txt");
try (FileWriter fw = new FileWriter(f, true); PrintWriter out = new PrintWriter(fw);) {
out.println("the name of the user is " + name + "\n");
out.println("the email of the user is " + email + "\n");
} catch (IOException e) {
e.printStackTrace();
}
}
PrintWriter buffers the data to be written so and will not write to disk until its buffer is full. Calling close() will ensure that any remaining data is flushed as well as closing the OutputStream.
close() statements typically appear in finally blocks.
Why the data was not being written to the file when I was not including that statement?
When the process terminates the unmanaged resources will be released. For InputStreams this is fine. For OutputStreams, you could lose an buffered data, so you should at least flush the stream before exiting the program.