Java7中文件IO发生了很大的变化,专门引入了很多新的类:
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
......等等,来取代原来的基于java.io.File的文件IO操作方式.
1. Path就是取代File的
A Path
represents a path that is hierarchical and composed of a sequence of directory and file name elements separated by a special separator or delimiter.
Path用于来表示文件路径和文件。可以有多种方法来构造一个Path对象来表示一个文件路径,或者一个文件:
1)首先是final类Paths的两个static方法,如何从一个路径字符串来构造Path对象:
Path path = Paths.get("C:/", "Xmp");
Path path2 = Paths.get("C:/Xmp");
URI u = URI.create("file:///C:/Xmp/dd");
Path p = Paths.get(u);
2)FileSystems构造:
Path path3 = FileSystems.getDefault().getPath("C:/", "access.log");
3)File和Path之间的转换,File和URI之间的转换:
File file = new File("C:/my.ini");
Path p1 = file.toPath();
p1.toFile();
file.toURI();
4)创建一个文件:
Path target2 = Paths.get("C:\\mystuff.txt");
// Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-rw-rw-");
// FileAttribute<Set<PosixFilePermission>> attrs = PosixFilePermissions.asFileAttribute(perms);
try {
if(!Files.exists(target2))
Files.createFile(target2);
} catch (IOException e) {
e.printStackTrace();
}
windows下不支持PosixFilePermission来指定rwx权限。
5)Files.newBufferedReader读取文件:
try {
// Charset.forName("GBK")
BufferedReader reader = Files.newBufferedReader(Paths.get("C:\\my.ini"), StandardCharsets.UTF_8);
String str = null;
while((str = reader.readLine()) != null){
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
可以看到使用 Files.newBufferedReader 远比原来的FileInputStream,然后BufferedReader包装,等操作简单的多了。
这里如果指定的字符编码不对,可能会抛出异常 MalformedInputException
java.nio.charset.MalformedInputException: Input length = 1
at java.nio.charset.CoderResult.throwException(CoderResult.java:281)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:339)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.coin.Test.main(Test.java:79)
6)文件写操作:
try {
BufferedWriter writer = Files.newBufferedWriter(Paths.get("C:\\my2.ini"), StandardCharsets.UTF_8);
writer.write("测试文件写操作");
writer.flush();
writer.close();
} catch (IOException e1) {
e1.printStackTrace();
}
7)遍历一个文件夹:
Path dir = Paths.get("D:\\webworkspace");
try(DirectoryStream<Path> stream = Files.newDirectoryStream(dir)){
for(Path e : stream){
System.out.println(e.getFileName());
}
}catch(IOException e){
}
try (Stream<Path> stream = Files.list(Paths.get("C:/"))){
Iterator<Path> ite = stream.iterator();
while(ite.hasNext()){
Path pp = ite.next();
System.out.println(pp.getFileName());
}
} catch (IOException e) {
e.printStackTrace();
}
上面是遍历单个目录,它不会遍历整个目录。遍历整个目录需要使用:Files.walkFileTree
8)遍历整个文件目录:
public static void main(String[] args) throws IOException{
Path startingDir = Paths.get("C:\\apache-tomcat-8.0.21");
List<Path> result = new LinkedList<Path>();
Files.walkFileTree(startingDir, new FindJavaVisitor(result));
System.out.println("result.size()=" + result.size());
}
private static class FindJavaVisitor extends SimpleFileVisitor<Path>{
private List<Path> result;
public FindJavaVisitor(List<Path> result){
this.result = result;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs){
if(file.toString().endsWith(".java")){
result.add(file.getFileName());
}
return FileVisitResult.CONTINUE;
}
}
来一个实际例子:
public static void main(String[] args) throws IOException {
Path startingDir = Paths.get("F:\\upload\\images"); // F:\\upload\\images\\2\\20141206
List<Path> result = new LinkedList<Path>();
Files.walkFileTree(startingDir, new FindJavaVisitor(result));
System.out.println("result.size()=" + result.size());
System.out.println("done.");
}
private static class FindJavaVisitor extends SimpleFileVisitor<Path>{
private List<Path> result;
public FindJavaVisitor(List<Path> result){
this.result = result;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs){
String filePath = file.toFile().getAbsolutePath();
if(filePath.matches(".*_[1|2]{1}\\.(?i)(jpg|jpeg|gif|bmp|png)")){
try {
Files.deleteIfExists(file);
} catch (IOException e) {
e.printStackTrace();
}
result.add(file.getFileName());
} return FileVisitResult.CONTINUE;
}
}
将目录下面所有符合条件的图片删除掉:filePath.matches(".*_[1|2]{1}\\.(?i)(jpg|jpeg|gif|bmp|png)")
public static void main(String[] args) throws IOException {
Path startingDir = Paths.get("F:\\111111\\upload\\images"); // F:\111111\\upload\\images\\2\\20141206
List<Path> result = new LinkedList<Path>();
Files.walkFileTree(startingDir, new FindJavaVisitor(result));
System.out.println("result.size()=" + result.size());
System.out.println("done.");
}
private static class FindJavaVisitor extends SimpleFileVisitor<Path>{
private List<Path> result;
public FindJavaVisitor(List<Path> result){
this.result = result;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs){
String filePath = file.toFile().getAbsolutePath();
int width = 224;
int height = 300;
StringUtils.substringBeforeLast(filePath, ".");
String newPath = StringUtils.substringBeforeLast(filePath, ".") + "_1."
+ StringUtils.substringAfterLast(filePath, ".");
try {
ImageUtil.zoomImage(filePath, newPath, width, height);
} catch (IOException e) {
e.printStackTrace();
return FileVisitResult.CONTINUE;
}
result.add(file.getFileName());
return FileVisitResult.CONTINUE;
}
}
为目录下的所有图片生成指定大小的缩略图。a.jpg 则生成 a_1.jpg
2. 强大的java.nio.file.Files
1)创建目录和文件:
try {
Files.createDirectories(Paths.get("C://TEST"));
if(!Files.exists(Paths.get("C://TEST")))
Files.createFile(Paths.get("C://TEST/test.txt"));
// Files.createDirectories(Paths.get("C://TEST/test2.txt"));
} catch (IOException e) {
e.printStackTrace();
}
注意创建目录和文件Files.createDirectories 和 Files.createFile不能混用,必须先有目录,才能在目录中创建文件。
2)文件复制:
从文件复制到文件:Files.copy(Path source, Path target, CopyOption options);
从输入流复制到文件:Files.copy(InputStream in, Path target, CopyOption options);
从文件复制到输出流:Files.copy(Path source, OutputStream out);
try {
Files.createDirectories(Paths.get("C://TEST"));
if(!Files.exists(Paths.get("C://TEST")))
Files.createFile(Paths.get("C://TEST/test.txt"));
// Files.createDirectories(Paths.get("C://TEST/test2.txt"));
Files.copy(Paths.get("C://my.ini"), System.out);
Files.copy(Paths.get("C://my.ini"), Paths.get("C://my2.ini"), StandardCopyOption.REPLACE_EXISTING);
Files.copy(System.in, Paths.get("C://my3.ini"), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
3)遍历一个目录和文件夹上面已经介绍了:Files.newDirectoryStream , Files.walkFileTree
4)读取文件属性:
Path zip = Paths.get(uri);
System.out.println(Files.getLastModifiedTime(zip));
System.out.println(Files.size(zip));
System.out.println(Files.isSymbolicLink(zip));
System.out.println(Files.isDirectory(zip));
System.out.println(Files.readAttributes(zip, "*"));
5)读取和设置文件权限:
Path profile = Paths.get("/home/digdeep/.profile");
PosixFileAttributes attrs = Files.readAttributes(profile, PosixFileAttributes.class);// 读取文件的权限
Set<PosixFilePermission> posixPermissions = attrs.permissions();
posixPermissions.clear();
String owner = attrs.owner().getName();
String perms = PosixFilePermissions.toString(posixPermissions);
System.out.format("%s %s%n", owner, perms);
posixPermissions.add(PosixFilePermission.OWNER_READ);
posixPermissions.add(PosixFilePermission.GROUP_READ);
posixPermissions.add(PosixFilePermission.OTHERS_READ);
posixPermissions.add(PosixFilePermission.OWNER_WRITE);
Files.setPosixFilePermissions(profile, posixPermissions); // 设置文件的权限
Files类简直强大的一塌糊涂,几乎所有文件和目录的相关属性,操作都有想要的api来支持。这里懒得再继续介绍了,详细参见 jdk8 的文档
3.path的join和resolve
1.连接路径:path.join([path1][, path2][, ...])
path.join()方法可以连接任意多个路径字符串。要连接的多个路径可做为参数传入。path.join()方法在接边路径的同时也会对路径进行规范化。例如:
var path = require('path');
//合法的字符串连接
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..')
// 连接后
'/foo/bar/baz/asdf'
//不合法的字符串将抛出异常
path.join('foo', {}, 'bar')
// 抛出的异常
TypeError: Arguments to path.join must be strings'
var path = require('path');
//合法的字符串连接
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..')
// 连接后
'/foo/bar/baz/asdf'
//不合法的字符串将抛出异常
path.join('foo', {}, 'bar')
// 抛出的异常
TypeError: Arguments to path.join must be strings'
2.路径解析:path.resolve([from ...], to)
path.resolve()方法可以将多个路径解析为一个规范化的绝对路径。其处理方式类似于对这些路径逐一进行cd操作,与cd操作不同的是,这引起路径可以是文件,并且可不必实际存在(resolve()方法不会利用底层的文件系统判断路径是否存在,而只是进行路径字符串操作)。例如:
path.resolve('foo/bar', '/tmp/file/', '..', 'a/../subfile')
相当于
cd foo/bar
cd /tmp/file/
cd ..
cd a/../subfile
pwd
例子:
path.resolve('/foo/bar', './baz')
// 输出结果为
'/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/')
// 输出结果为
'/tmp/file'
path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif')
// 当前的工作路径是 /home/itbilu/node,则输出结果为
'/home/itbilu/node/wwwroot/static_files/gif/image.gif'
path.resolve('/foo/bar', './baz')
// 输出结果为
'/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/')
// 输出结果为
'/tmp/file'
path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif')
// 当前的工作路径是 /home/itbilu/node,则输出结果为
'/home/itbilu/node/wwwroot/static_files/gif/image.gif'
3.对比
const path = require('path');
let myPath = path.join(__dirname,'/img/so');
let myPath2 = path.join(__dirname,'./img/so');
let myPath3 = path.resolve(__dirname,'/img/so');
let myPath4 = path.resolve(__dirname,'./img/so');
console.log(__dirname); //D:\myProgram\test
console.log(myPath); //D:\myProgram\test\img\so
console.log(myPath2); //D:\myProgram\test\img\so
console.log(myPath3); //D:\img\so<br>
console.log(myPath4); //D:\myProgram\test\img\so
const path = require('path');
let myPath = path.join(__dirname,'/img/so');
let myPath2 = path.join(__dirname,'./img/so');
let myPath3 = path.resolve(__dirname,'/img/so');
let myPath4 = path.resolve(__dirname,'./img/so');
console.log(__dirname); //D:\myProgram\test
console.log(myPath); //D:\myProgram\test\img\so
console.log(myPath2); //D:\myProgram\test\img\so
console.log(myPath3); //D:\img\so<br>
console.log(myPath4); //D:\myProgram\test\img\so