Migrating from java.io.File to Java 7 NIO (java.nio.file.Path)
These examples assume that you already know what Java 7's NIO is in general, and you are used to writing code using java.io.File. Use these examples as a means to quickly find more NIO-centric documentation for migrating.
// Forward slashes can be used in place of backslashes even on a Windows operating system// -> IOFile folder =newFile("C:/");File fileInFolder =newFile(folder,"io.txt");// -> NIOPath directory =Paths.get("C:/");Path pathInDirectory = directory.resolve("nio.txt");
Converting File from/to Path for use with libraries
// -> IO to NIOPath pathFromFile =newFile("io.txt").toPath();// -> NIO to IOFile fileFromPath =Paths.get("nio.txt").toFile();
There are several ways to write and read from a file using NIO for different performance and memory constraints, readability and use cases, such as FileChannel(opens new window), Files.write(Path path, byte\[\] bytes, OpenOption... options)(opens new window)... In this example, only OutputStream is covered, but you are strongly encouraged to learn about memory-mapped files and the various static methods available in java.nio.file.Files.
List<String> lines =Arrays.asList(String.valueOf(Calendar.getInstance().getTimeInMillis()),"line one","line two");// -> IOif(file.exists()){// Note: Not atomicthrownewIOException("File already exists");}try(FileOutputStream outputStream =newFileOutputStream(file)){for(String line : lines){
outputStream.write((line +System.lineSeparator()).getBytes(StandardCharsets.UTF_8));}}// -> NIOtry(OutputStream outputStream =Files.newOutputStream(path,StandardOpenOption.CREATE_NEW)){for(String line : lines){
outputStream.write((line +System.lineSeparator()).getBytes(StandardCharsets.UTF_8));}}
Iterating on each file within a folder
// -> IOfor(File selectedFile : folder.listFiles()){// Note: Depending on the number of files in the directory folder.listFiles() may take a long time to returnSystem.out.println((selectedFile.isDirectory()?"d":"f")+" "+ selectedFile.getAbsolutePath());}// -> NIOFiles.walkFileTree(directory,EnumSet.noneOf(FileVisitOption.class),1,newSimpleFileVisitor<Path>(){@OverridepublicFileVisitResultpreVisitDirectory(Path selectedPath,BasicFileAttributes attrs)throwsIOException{System.out.println("d "+ selectedPath.toAbsolutePath());returnFileVisitResult.CONTINUE;}@OverridepublicFileVisitResultvisitFile(Path selectedPath,BasicFileAttributes attrs)throwsIOException{System.out.println("f "+ selectedPath.toAbsolutePath());returnFileVisitResult.CONTINUE;}});
Recursive folder iteration
// -> IOrecurseFolder(folder);// -> NIO// Note: Symbolic links are NOT followed unless explicitly passed as an argument to Files.walkFileTreeFiles.walkFileTree(directory,newSimpleFileVisitor<Path>(){@OverridepublicFileVisitResultpreVisitDirectory(Path dir,BasicFileAttributes attrs)throwsIOException{System.out.println("d "+ selectedPath.toAbsolutePath());returnFileVisitResult.CONTINUE;}@OverridepublicFileVisitResultvisitFile(Path selectedPath,BasicFileAttributes attrs)throwsIOException{System.out.println("f "+ selectedPath.toAbsolutePath());returnFileVisitResult.CONTINUE;}});privatestaticvoidrecurseFolder(File folder){for(File selectedFile : folder.listFiles()){System.out.println((selectedFile.isDirectory()?"d":"f")+" "+ selectedFile.getAbsolutePath());if(selectedFile.isDirectory()){// Note: Symbolic links are followedrecurseFolder(selectedFile);}}}
Note, that since Java 1.7 the try-with-resources(opens new window) statement was introduced what made implementation of reading\writing operation much simpler:
Write to a file test.txt:
String filepath ="C:\\test.txt";try(FileOutputStream fos =newFileOutputStream(filepath)){byte[] buffer ="This will be written in test.txt".getBytes();
fos.write(buffer,0, buffer.length);}catch(FileNotFoundException e){
e.printStackTrace();}catch(IOException e){
e.printStackTrace();}
// Read a single byte from the streamint b = inputStream.read();if(b >=0){// A negative value represents the end of the stream, normal values are in the range 0 - 255// Write the byte to another stream
outputStream.write(b);}// Read a chunkbyte[] data =newbyte[1024];int nBytesRead = inputStream.read(data);if(nBytesRead >=0){// A negative value represents end of stream// Write the chunk to another stream
outputStream.write(data,0, nBytesRead);}
There are some exceptions, probably most notably the PrintStream(opens new window) which adds the "ability to print representations of various data values conveniently". This allows to use System.out(opens new window) both as a binary InputStream and as a textual output using methods such as System.out.println().
With the Writer(opens new window) and Reader(opens new window) classes, Java also provides an API for explicit character streams. Although most applications will base these implementations on streams, the character stream API does not expose any methods for binary content.
// This example uses the platform's default charset, see below// for a better implementation.Writer writer =newOutputStreamWriter(System.out);
writer.write("Hello world!");Reader reader =newInputStreamReader(System.in);char singleCharacter = reader.read();
Whenever it is necessary to encode characters into binary data (e.g. when using the InputStreamWriter / OutputStreamWriter classes), you should specify a charset if you do not want to depend on the platform's default charset. When in doubt, use a Unicode-compatible encoding, e.g. UTF-8 which is supported on all Java platforms. Therefore, you should probably stay away from classes like FileWriter and FileReader as those always use the default platform charset. A better way to access files using character streams is this:
One of the most commonly used Readers is BufferedReader which provides a method to read whole lines of text from another reader and is presumably the simplest way to read a character stream line by line:
// Read from baseReader, one line at a timeBufferedReader reader =newBufferedReader( baseReader );String line;while((line = reader.readLine())!=null){// Remember: System.out is a stream, not a writer!System.out.println(line);}
Reading a file with a Scanner
Reading a file line by line
publicclassMain{publicstaticvoidmain(String[] args){try{Scanner scanner =newScanner(newFile("example.txt"));while(scanner.hasNextLine()){String line = scanner.nextLine();//do stuff}}catch(FileNotFoundException e){
e.printStackTrace();}}}
word by word
publicclassMain{publicstaticvoidmain(String[] args){try{Scanner scanner =newScanner(newFile("example.txt"));while(scanner.hasNext()){String line = scanner.next();//do stuff}}catch(FileNotFoundException e){
e.printStackTrace();}}}
and you can also change the delimeter by using scanner.useDelimeter() method
Reading a whole file at once
File f =newFile(path);String content =newScanner(f).useDelimiter("\\Z").next();
\Z is the EOF (End of File) Symbol. When set as delimiter the Scanner will read the fill until the EOF Flag is reached.
Iterating over a directory and filter by file extension
You can read an a binary file using this piece of code in all recent versions of Java:
File file =newFile("path_to_the_file");byte[] data =newbyte[(int) file.length()];DataInputStream stream =newDataInputStream(newFileInputStream(file));
stream.readFully(data);
stream.close();
If you are using Java 7 or later, there is a simpler way using the nio API:
Path path =Paths.get("path_to_the_file");byte[] data =Files.readAllBytes(path);
Locking
A file can be locked using the FileChannel API that can be acquired from Input Output streams and readers
Example with streams
// Open a file stream
FileInputStream ios = new FileInputStream(filename);
// get underlying channel
FileChannel channel = ios.getChannel();
/*
* try to lock the file. true means whether the lock is shared or not i.e. multiple processes can acquire a
* shared lock (for reading only) Using false with readable channel only will generate an exception. You should
* use a writable channel (taken from FileOutputStream) when using false. tryLock will always return immediately
*/
FileLock lock = channel.tryLock(0, Long.MAX_VALUE, true);
if (lock == null) {
System.out.println("Unable to acquire lock");
} else {
System.out.println("Lock acquired successfully");
}
// you can also use blocking call which will block until a lock is acquired.
channel.lock();
// Once you have completed desired operations of file. release the lock
if (lock != null) {
lock.release();
}
// close the file stream afterwards
// Example with reader
RandomAccessFile randomAccessFile = new RandomAccessFile(filename, "rw");
FileChannel channel = randomAccessFile.getChannel();
//repeat the same steps as above but now you can use shared as true or false as the channel is in read write mode
Copying a file using InputStream and OutputStream
We can directly copy data from a source to a data sink using a loop. In this example, we are reading data from an InputStream and at the same time, writing to an OutputStream. Once we are done reading and writing, we have to close the resource.
Channel uses a Buffer to read/write data. A buffer is a fixed sized container where we can write a block of data at once. Channel is a quite faster than stream-based I/O.
To read data from a file using Channel we need to have the following steps-
We need an instance of FileInputStream. FileInputStreamhas a method named getChannel() which returns a Channel.
Call the getChannel() method of FileInputStream and acquire Channel.
Create a ByteBuffer. ByteBuffer is a fixed size container of bytes.
Channel has a read method and we have to provide a ByteBuffer as an argument to this read method. ByteBuffer has two modes - read-only mood and write-only mood. We can change the mode using flip() method call. Buffer has a position, limit, and capacity. Once a buffer is created with a fixed size, its limit and capacity are the same as the size and the position starts from zero. While a buffer is written with data, its position gradually increases. Changing mode means, changing the position. To read data from the beginning of a buffer, we have to set the position to zero. flip() method change the position
When we call the read method of the Channel, it fills up the buffer using data.
If we need to read the data from the ByteBuffer, we need to flip the buffer to change its mode to write-only to read-only mode and then keep reading data from the buffer.
When there is no longer data to read, the read() method of channel returns 0 or -1.
Reading file using a BufferedInputStream generally faster than FileInputStream because it maintains an internal buffer to store bytes read from the underlying input stream.
We can use PrintStream class to write a file. It has several methods that let you print any data type values. println() method appends a new line.
Once we are done printing, we have to flush the PrintStream.
importjava.io.FileNotFoundException;importjava.io.PrintStream;importjava.time.LocalDate;publicclassFileWritingDemo{publicstaticvoidmain(String[] args){String destination ="file1.txt";try(PrintStream ps =newPrintStream(destination)){
ps.println("Stackoverflow documentation seems fun.");
ps.println();
ps.println("I love Java!");
ps.printf("Today is: %1$tm/%1$td/%1$tY",LocalDate.now());
ps.flush();}catch(FileNotFoundException e){
e.printStackTrace();}}}
Iterate over a directory printing subdirectories in it
public void iterate(final String dirPath) throws IOException {
final DirectoryStream<Path> paths = Files.newDirectoryStream(Paths.get(dirPath));
for (final Path path : paths) {
if (Files.isDirectory(path)) {
System.out.println(path.getFileName());
}
}
}
Adding Directories
To make a new directory from a File instance you would need to use one of two methods: mkdirs() or mkdir().
mkdirs() - Creates the directory named by this abstract pathname, including any necessary but nonexistent parent directories. Note that if this operation fails it may have succeeded in creating some of the necessary parent directories. (source(opens new window))
Note:createNewFile() will not create a new directory only a file.
File singleDir =newFile("C:/Users/SomeUser/Desktop/A New Folder/");File multiDir =newFile("C:/Users/SomeUser/Desktop/A New Folder 2/Another Folder/");// assume that neither "A New Folder" or "A New Folder 2" exist
singleDir.createNewFile();// will make a new file called "A New Folder.file"
singleDir.mkdir();// will make the directory
singleDir.mkdirs();// will make the directory
multiDir.createNewFile();// will throw a IOException
multiDir.mkdir();// will not work
multiDir.mkdirs();// will make the directory
Blocking or redirecting standard output / error
Sometimes a poorly designed 3rd-party library will write unwanted diagnostics to System.out or System.err streams. The recommended solutions to this would be to either find a better library or (in the case of open source) fix the problem and contribute a patch to the developers.
If the above solutions are not feasible, then you should consider redirecting the streams.
Redirection on the command line
On a UNIX, Linux or MacOSX system can be done from the shell using > redirection. For example:
The first one redirects standard output and standard error to "/dev/null", which throws away anything written to those streams. The second of redirects standard output to "out.log" and standard error to "error.log".
(For more information on redirection, refer to the documentation of the command shell you are using. Similar advice applies to Windows.)
Alternatively, you could implement the redirection in a wrapper script or batch file that launches the Java application.
Redirection within a Java application
It is also possible to redired the streams within a Java application using System.setOut() and System.setErr(). For example, the following snippet redirects standard output and standard error to 2 log files:
If you want to throw away the output entirely, you can create an output stream that "writes" to an invalid file descriptor. This is functionally equivalent to writing to "/dev/null" on UNIX.
Caution: be careful how you use setOut and setErr:
The redirection will affect the entire JVM.
By doing this, you are taking away the user's ability to redirect the streams from the command line.
Accessing the contents of a ZIP file
The FileSystem API of Java 7 allows to read and add entries from or to a Zip file using the Java NIO file API in the same way as operating on any other filesystem.
The FileSystem is a resource that should be properly closed after use, therefore the try-with-resources block should be used.
Reading from an existing file
Path pathToZip =Paths.get("path/to/file.zip");try(FileSystem zipFs =FileSystems.newFileSystem(pathToZip,null)){Path root = zipFs.getPath("/");...//access the content of the zip file same as ordinary files}catch(IOException ex){
ex.printStackTrace();}
Creating a new file
Map<String,String> env =newHashMap<>();
env.put("create","true");//required for creating a new zip file
env.put("encoding","UTF-8");//optional: default is UTF-8URI uri = URI.create("jar:file:/path/to/file.zip");try(FileSystem zipfs =FileSystems.newFileSystem(uri, env)){Path newFile = zipFs.getPath("/newFile.txt");//writing to fileFiles.write(newFile,"Hello world".getBytes());}catch(IOException ex){
ex.printStackTrace();}