正确地处理文件
拼接文件路径:使用Path,且文件分隔符使用File.pathSeparator,不要硬编码为/
或者\
//假设有如下文件:/Users/yue/Desktop/test.ev4
Path path = Paths.get("/Users", "yue", "Desktop", "test.ev4");
//注意,这种方式是错误的,即Path只会从第二个参数开始,自动添加文件分隔符,且第一个参数应该是有效路径
Path path = Paths.get(File.pathSeparator, "Users", "yue", "Desktop", "test.ev4");
读取文件所有内容前,先判断文件大小,防止OOM
public static byte[] readAllBytes(String fileName, long maxSize) throws IOException {
Path path = Paths.get(fileName);
long size = Files.size(path);
if (size > maxSize) {
throw new IOException("file: " + path + ", size:" + size + "> " + maxSize);
}
return Files.readAllBytes(path);
}
public static List<String> readAllLines(String fileName, Charset charset, long maxSize) throws IOException {
Path path = Paths.get(fileName);
long size = Files.size(path);
if (size > maxSize) {
throw new IOException("file: " + path + ", size:" + size + "> " + maxSize);
}
return Files.readAllLines(path, charset);
}
遍历文件:使用FileVisitor
Path startingDir = Paths.get("/Users/yue/Desktop");
try {
Files.walkFileTree(startingDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
System.out.println(path.getFileName());
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
e.printStackTrace();
}
判断文件是否在某路径下:使用file.getCanonicalPath(),不必递归
//使用file.getCanonicalPath()
public static boolean isSubFile(File dir, File file) throws IOException {
return file.getCanonicalPath().startsWith(dir.getCanonicalPath() + File.pathSeparator);
}
//递归方式
public static boolean isInDirectory(File dir, File file) {
if (file == null)
return false;
if (file.equals(dir))
return true;
return isInDirectory(dir, file.getParentFile());
}
监视文件改变:使用WatchService
public class Configs {
public static Properties properties;
static {
final String configFilename = "app.properties";
final ClassPathResource resource = new ClassPathResource(configFilename);
try {
//先加载一次配置文件
properties = PropertiesLoaderUtils.loadProperties(resource);
//设置监听配置文件所在目录的文件删除/新增操作
final WatchService watcher = FileSystems.getDefault().newWatchService();
Paths.get(resource.getFile().getParent()).register(watcher,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
//开启监听线程
Thread watchThread = new Thread(() -> {
try {
WatchKey watchKey = watcher.take();
for (WatchEvent<?> event : watchKey.pollEvents()) {
//如果是配置文件发生变化,则重新加载配置文件
System.out.println(event.kind() + ":::" + event.context().toString());
if (Objects.equals(configFilename, event.context().toString())) {
properties = PropertiesLoaderUtils.loadProperties(resource);
break;
}
}
watchKey.reset();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
});
watchThread.setDaemon(true);
watchThread.start();
//JVM停止时,关闭watcher线程
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
watcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}));
} catch (IOException e) {
e.printStackTrace();
}
}
public static boolean getBoolean(String key) {
String property = properties.getProperty(key, "false");
return "true".equals(property);
}
public static int getInt(String key) {
String property = properties.getProperty(key, "0");
return Integer.valueOf(property);
}
}
Web服务器防止非法的文件路径访问
字符截断攻击和文件历遍漏洞原理:在文件名中插入%00的URL编码,web服务器会把%00后面的内容抛弃。