Hutool-core最新最全解析(二)

文件工具类-FileUtil

在IO操作中,文件的操作相对来说是比较复杂的,但也是使用频率最高的部分,我们几乎所有的项目中几乎都躺着一个叫做FileUtil或者FileUtils的工具类,我想Hutool应该将这个工具类纳入其中,解决用来解决大部分的文件操作问题。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package cn.hutool.core.io;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.file.FileCopier;
import cn.hutool.core.io.file.FileMode;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.io.file.LineSeparator;
import cn.hutool.core.io.file.PathUtil;
import cn.hutool.core.io.file.Tailer;
import cn.hutool.core.io.file.FileReader.ReaderHandler;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.io.unit.DataSizeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.core.util.ZipUtil;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.Checksum;

public class FileUtil extends PathUtil {
    public static final String CLASS_EXT = ".class";
    public static final String JAR_FILE_EXT = ".jar";
    public static final String JAR_PATH_EXT = ".jar!";
    public static final String PATH_FILE_PRE = "file:";
    public static final String FILE_SEPARATOR;
    public static final String PATH_SEPARATOR;
    private static final Pattern PATTERN_PATH_ABSOLUTE;

    public FileUtil() {
    }
	// 是否为Windows环境
    public static boolean isWindows() {
        return '\\' == File.separatorChar;
    }
	// 列出指定路径下的目录和文件 给定的绝对路径不能是压缩包中的路径
    public static File[] ls(String path) {
        if (path == null) {
            return null;
        } else {
            File file = file(path);
            if (file.isDirectory()) {
                return file.listFiles();
            } else {
                throw new IORuntimeException(StrUtil.format("Path [{}] is not directory!", new Object[]{path}));
            }
        }
    }
	// 文件是否为空
    public static boolean isEmpty(File file) {
        if (null != file && file.exists()) {
            if (file.isDirectory()) {
                String[] subFiles = file.list();
                return ArrayUtil.isEmpty(subFiles);
            } else if (file.isFile()) {
                return file.length() <= 0L;
            } else {
                return false;
            }
        } else {
            return true;
        }
    }
	// 文件不能为空
    public static boolean isNotEmpty(File file) {
        return !isEmpty(file);
    }
	// 目录是否为空
    public static boolean isDirEmpty(File dir) {
        return isDirEmpty(dir.toPath());
    }
	// 递归遍历目录以及子目录中的所有文件
    public static List<File> loopFiles(String path, FileFilter fileFilter) {
        return loopFiles(file(path), fileFilter);
    }
	// 递归遍历目录以及子目录中的所有文件
    public static List<File> loopFiles(File file, FileFilter fileFilter) {
        return loopFiles(file, -1, fileFilter);
    }
	// 递归遍历目录并处理目录下的文件,可以处理目录或文件: 非目录则直接调用Consumer处理 目录则递归调用此方法处理
    public static void walkFiles(File file, Consumer<File> consumer) {
        if (file.isDirectory()) {
            File[] subFiles = file.listFiles();
            if (ArrayUtil.isNotEmpty(subFiles)) {
                File[] var3 = subFiles;
                int var4 = subFiles.length;

                for(int var5 = 0; var5 < var4; ++var5) {
                    File tmp = var3[var5];
                    walkFiles(tmp, consumer);
                }
            }
        } else {
            consumer.accept(file);
        }

    }
	// 递归遍历目录以及子目录中的所有文件 如果提供file为文件,直接返回过滤结果
    public static List<File> loopFiles(File file, int maxDepth, FileFilter fileFilter) {
        return loopFiles(file.toPath(), maxDepth, fileFilter);
    }
	// 递归遍历目录以及子目录中的所有文件 如果用户传入相对路径,则是相对classpath的路径
    public static List<File> loopFiles(String path) {
        return loopFiles(file(path));
    }
	// 递归遍历目录以及子目录中的所有文件
    public static List<File> loopFiles(File file) {
        return loopFiles((File)file, (FileFilter)null);
    }
	// 获得指定目录下所有文件 不会扫描子目录 如果用户传入相对路径,则是相对classpath的路径
    public static List<String> listFileNames(String path) throws IORuntimeException {
        if (path == null) {
            return new ArrayList(0);
        } else {
            int index = path.lastIndexOf(".jar!");
            if (index < 0) {
                List<String> paths = new ArrayList();
                File[] files = ls(path);
                File[] var4 = files;
                int var5 = files.length;

                for(int var6 = 0; var6 < var5; ++var6) {
                    File file = var4[var6];
                    if (file.isFile()) {
                        paths.add(file.getName());
                    }
                }

                return paths;
            } else {
                path = getAbsolutePath(path);
                index += ".jar".length();
                JarFile jarFile = null;

                List var3;
                try {
                    jarFile = new JarFile(path.substring(0, index));
                    var3 = ZipUtil.listFileNames(jarFile, StrUtil.removePrefix(path.substring(index + 1), "/"));
                } catch (IOException var11) {
                    throw new IORuntimeException(StrUtil.format("Can not read file path of [{}]", new Object[]{path}), var11);
                } finally {
                    IoUtil.close(jarFile);
                }

                return var3;
            }
        }
    }
	// 创建File对象,相当于调用new File(),不做任何处理
    public static File newFile(String path) {
        return new File(path);
    }
	// 创建File对象,自动识别相对或绝对路径,相对路径将自动从ClassPath下寻找
    public static File file(String path) {
        return null == path ? null : new File(getAbsolutePath(path));
    }
	// 创建File对象
    public static File file(String parent, String path) {
        return file(new File(parent), path);
    }
	// 创建File对象
    public static File file(File parent, String path) {
        if (StrUtil.isBlank(path)) {
            throw new NullPointerException("File path is blank!");
        } else {
            return checkSlip(parent, buildFile(parent, path));
        }
    }
	// 通过多层目录参数创建文件
    public static File file(File directory, String... names) {
        Assert.notNull(directory, "directory must not be null", new Object[0]);
        if (ArrayUtil.isEmpty(names)) {
            return directory;
        } else {
            File file = directory;
            String[] var3 = names;
            int var4 = names.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                String name = var3[var5];
                if (null != name) {
                    file = file(file, name);
                }
            }

            return file;
        }
    }
	// 通过多层目录创建文件
    public static File file(String... names) {
        if (ArrayUtil.isEmpty(names)) {
            return null;
        } else {
            File file = null;
            String[] var2 = names;
            int var3 = names.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                String name = var2[var4];
                if (file == null) {
                    file = file(name);
                } else {
                    file = file(file, name);
                }
            }

            return file;
        }
    }
	// 创建File对象
    public static File file(URI uri) {
        if (uri == null) {
            throw new NullPointerException("File uri is null!");
        } else {
            return new File(uri);
        }
    }
	// 创建File对象
    public static File file(URL url) {
        return new File(URLUtil.toURI(url));
    }
	// 获取临时文件路径(绝对路径)
    public static String getTmpDirPath() {
        return System.getProperty("java.io.tmpdir");
    }
	// 获取临时文件目录
    public static File getTmpDir() {
        return file(getTmpDirPath());
    }
	// 获取用户路径(绝对路径)
    public static String getUserHomePath() {
        return System.getProperty("user.home");
    }
	// 获取用户目录
    public static File getUserHomeDir() {
        return file(getUserHomePath());
    }
	// 判断文件是否存在,如果path为null,则返回false
    public static boolean exist(String path) {
        return null != path && file(path).exists();
    }
	// 判断文件是否存在,如果file为null,则返回false
    public static boolean exist(File file) {
        return null != file && file.exists();
    }
	// 是否存在匹配文件
    public static boolean exist(String directory, String regexp) {
        File file = new File(directory);
        if (!file.exists()) {
            return false;
        } else {
            String[] fileList = file.list();
            if (fileList == null) {
                return false;
            } else {
                String[] var4 = fileList;
                int var5 = fileList.length;

                for(int var6 = 0; var6 < var5; ++var6) {
                    String fileName = var4[var6];
                    if (fileName.matches(regexp)) {
                        return true;
                    }
                }

                return false;
            }
        }
    }
	// 指定文件最后修改时间
    public static Date lastModifiedTime(File file) {
        return !exist(file) ? null : new Date(file.lastModified());
    }
	// 指定文件最后修改时间
    public static Date lastModifiedTime(String path) {
        return lastModifiedTime(new File(path));
    }
	// 计算目录或文件的总大小
    public static long size(File file) {
        return size(file, false);
    }
	// 计算目录或文件的总大小
    public static long size(File file, boolean includeDirSize) {
        if (null != file && file.exists() && !isSymlink(file)) {
            if (!file.isDirectory()) {
                return file.length();
            } else {
                long size = includeDirSize ? file.length() : 0L;
                File[] subFiles = file.listFiles();
                if (ArrayUtil.isEmpty(subFiles)) {
                    return 0L;
                } else {
                    File[] var5 = subFiles;
                    int var6 = subFiles.length;

                    for(int var7 = 0; var7 < var6; ++var7) {
                        File subFile = var5[var7];
                        size += size(subFile, includeDirSize);
                    }

                    return size;
                }
            }
        } else {
            return 0L;
        }
    }
	// 计算文件的总行数 读取文件采用系统默认编码,一般乱码不会造成行数错误。
    public static int getTotalLines(File file) {
        if (!isFile(file)) {
            throw new IORuntimeException("Input must be a File");
        } else {
            try {
                LineNumberReader lineNumberReader = new LineNumberReader(new FileReader(file));
                Throwable var2 = null;

                int var3;
                try {
                    lineNumberReader.setLineNumber(1);
                    lineNumberReader.skip(9223372036854775807L);
                    var3 = lineNumberReader.getLineNumber();
                } catch (Throwable var13) {
                    var2 = var13;
                    throw var13;
                } finally {
                    if (lineNumberReader != null) {
                        if (var2 != null) {
                            try {
                                lineNumberReader.close();
                            } catch (Throwable var12) {
                                var2.addSuppressed(var12);
                            }
                        } else {
                            lineNumberReader.close();
                        }
                    }

                }

                return var3;
            } catch (IOException var15) {
                throw new IORuntimeException(var15);
            }
        }
    }
	// 给定文件或目录的最后修改时间是否晚于给定时间
    public static boolean newerThan(File file, File reference) {
        return null != reference && reference.exists() ? newerThan(file, reference.lastModified()) : true;
    }
	// 给定文件或目录的最后修改时间是否晚于给定时间
    public static boolean newerThan(File file, long timeMillis) {
        if (null != file && file.exists()) {
            return file.lastModified() > timeMillis;
        } else {
            return false;
        }
    }
	// 创建文件及其父目录,如果这个文件存在,直接返回这个文件 此方法不对File对象类型做判断,如果File不存在,无法判断其类型
    public static File touch(String path) throws IORuntimeException {
        return path == null ? null : touch(file(path));
    }
	// 创建文件及其父目录,如果这个文件存在,直接返回这个文件 此方法不对File对象类型做判断,如果File不存在,无法判断其类型
    public static File touch(File file) throws IORuntimeException {
        if (null == file) {
            return null;
        } else {
            if (!file.exists()) {
                mkParentDirs(file);

                try {
                    file.createNewFile();
                } catch (Exception var2) {
                    throw new IORuntimeException(var2);
                }
            }

            return file;
        }
    }
	// 创建文件及其父目录,如果这个文件存在,直接返回这个文件 此方法不对File对象类型做判断,如果File不存在,无法判断其类型
    public static File touch(File parent, String path) throws IORuntimeException {
        return touch(file(parent, path));
    }
	// 创建文件及其父目录,如果这个文件存在,直接返回这个文件 此方法不对File对象类型做判断,如果File不存在,无法判断其类型
    public static File touch(String parent, String path) throws IORuntimeException {
        return touch(file(parent, path));
    }
	// 创建所给文件或目录的父目录
    public static File mkParentDirs(File file) {
        return null == file ? null : mkdir(getParent((File)file, 1));
    }
	// 创建所给文件或目录的父目录
    public static File mkParentDirs(String path) {
        return path == null ? null : mkParentDirs(file(path));
    }
	// 删除文件或者文件夹 路径如果为相对路径,会转换为ClassPath路径!
    public static boolean del(String fullFileOrDirPath) throws IORuntimeException {
        return del(file(fullFileOrDirPath));
    }
	// 删除文件或者文件夹
    public static boolean del(File file) throws IORuntimeException {
        if (file != null && file.exists()) {
            if (file.isDirectory()) {
                boolean isOk = clean(file);
                if (!isOk) {
                    return false;
                }
            }

            Path path = file.toPath();

            try {
                delFile(path);
            } catch (DirectoryNotEmptyException var3) {
                del((Path)path);
            } catch (IOException var4) {
                throw new IORuntimeException(var4);
            }

            return true;
        } else {
            return true;
        }
    }
	// 清空文件夹 注意:清空文件夹时不会判断文件夹是否为空,如果不空则递归删除子文件或文件夹 某个文件删除失败会终止删除操作
    public static boolean clean(String dirPath) throws IORuntimeException {
        return clean(file(dirPath));
    }
	//  清空文件夹 注意:清空文件夹时不会判断文件夹是否为空,如果不空则递归删除子文件或文件夹 某个文件删除失败会终止删除操作
    public static boolean clean(File directory) throws IORuntimeException {
        if (directory != null && directory.exists() && directory.isDirectory()) {
            File[] files = directory.listFiles();
            if (null != files) {
                File[] var2 = files;
                int var3 = files.length;

                for(int var4 = 0; var4 < var3; ++var4) {
                    File childFile = var2[var4];
                    if (!del(childFile)) {
                        return false;
                    }
                }
            }

            return true;
        } else {
            return true;
        }
    }
	// 清理空文件夹 此方法用于递归删除空的文件夹,不删除文件 如果传入的文件夹本身就是空的,删除这个文件夹
    public static boolean cleanEmpty(File directory) throws IORuntimeException {
        if (directory != null && directory.exists() && directory.isDirectory()) {
            File[] files = directory.listFiles();
            if (ArrayUtil.isEmpty(files)) {
                return directory.delete();
            } else {
                File[] var2 = files;
                int var3 = files.length;

                for(int var4 = 0; var4 < var3; ++var4) {
                    File childFile = var2[var4];
                    cleanEmpty(childFile);
                }

                return true;
            }
        } else {
            return true;
        }
    }
	// 创建文件夹,如果存在直接返回此文件夹 此方法不对File对象类型做判断,如果File不存在,无法判断其类型
    public static File mkdir(String dirPath) {
        if (dirPath == null) {
            return null;
        } else {
            File dir = file(dirPath);
            return mkdir(dir);
        }
    }
  // 创建文件夹,如果存在直接返回此文件夹 此方法不对File对象类型做判断,如果File不存在,无法判断其类型
    public static File mkdir(File dir) {
        if (dir == null) {
            return null;
        } else {
            if (!dir.exists()) {
                mkdirsSafely(dir, 5, 1L);
            }

            return dir;
        }
    }
	// 安全地级联创建目录 (确保并发环境下能创建成功)
    public static boolean mkdirsSafely(File dir, int tryCount, long sleepMillis) {
        if (dir == null) {
            return false;
        } else if (dir.isDirectory()) {
            return true;
        } else {
            for(int i = 1; i <= tryCount; ++i) {
                dir.mkdirs();
                if (dir.exists()) {
                    return true;
                }

                ThreadUtil.sleep(sleepMillis);
            }

            return dir.exists();
        }
    }
	// 创建临时文件
    public static File createTempFile(File dir) throws IORuntimeException {
        return createTempFile("hutool", (String)null, dir, true);
    }
	// 创建临时文件
    public static File createTempFile() throws IORuntimeException {
        return createTempFile("hutool", (String)null, (File)null, true);
    }
	// 创建临时文件
    public static File createTempFile(String suffix, boolean isReCreat) throws IORuntimeException {
        return createTempFile("hutool", suffix, (File)null, isReCreat);
    }
	// 创建临时文件
    public static File createTempFile(String prefix, String suffix, boolean isReCreat) throws IORuntimeException {
        return createTempFile(prefix, suffix, (File)null, isReCreat);
    }
	// 创建临时文件
    public static File createTempFile(File dir, boolean isReCreat) throws IORuntimeException {
        return createTempFile("hutool", (String)null, dir, isReCreat);
    }
	// 创建临时文件
    public static File createTempFile(String prefix, String suffix, File dir, boolean isReCreat) throws IORuntimeException {
        int exceptionsCount = 0;

        while(true) {
            try {
                File file = File.createTempFile(prefix, suffix, mkdir(dir)).getCanonicalFile();
                if (isReCreat) {
                    file.delete();
                    file.createNewFile();
                }

                return file;
            } catch (IOException var6) {
                ++exceptionsCount;
                if (exceptionsCount >= 50) {
                    throw new IORuntimeException(var6);
                }
            }
        }
    }
	// 复制文件
    public static File copyFile(String src, String dest, StandardCopyOption... options) throws IORuntimeException {
        Assert.notBlank(src, "Source File path is blank !", new Object[0]);
        Assert.notBlank(dest, "Destination File path is blank !", new Object[0]);
        return copyFile((Path)Paths.get(src), (Path)Paths.get(dest), options).toFile();
    }
	// 复制文件
    public static File copyFile(File src, File dest, StandardCopyOption... options) throws IORuntimeException {
        Assert.notNull(src, "Source File is null !", new Object[0]);
        if (!src.exists()) {
            throw new IORuntimeException("File not exist: " + src);
        } else {
            Assert.notNull(dest, "Destination File or directiory is null !", new Object[0]);
            if (equals(src, dest)) {
                throw new IORuntimeException("Files '{}' and '{}' are equal", new Object[]{src, dest});
            } else {
                return copyFile((Path)src.toPath(), (Path)dest.toPath(), options).toFile();
            }
        }
    }
	// 复制文件或目录
    public static File copy(String srcPath, String destPath, boolean isOverride) throws IORuntimeException {
        return copy(file(srcPath), file(destPath), isOverride);
    }
	// 复制文件或目录
    public static File copy(File src, File dest, boolean isOverride) throws IORuntimeException {
        return FileCopier.create(src, dest).setOverride(isOverride).copy();
    }
	// 复制文件或目录
    public static File copyContent(File src, File dest, boolean isOverride) throws IORuntimeException {
        return FileCopier.create(src, dest).setCopyContentIfDir(true).setOverride(isOverride).copy();
    }
	// 复制文件或目录
    public static File copyFilesFromDir(File src, File dest, boolean isOverride) throws IORuntimeException {
        return FileCopier.create(src, dest).setCopyContentIfDir(true).setOnlyCopyFile(true).setOverride(isOverride).copy();
    }
	// 移动文件或者目录
    public static void move(File src, File target, boolean isOverride) throws IORuntimeException {
        Assert.notNull(src, "Src file must be not null!", new Object[0]);
        Assert.notNull(target, "target file must be not null!", new Object[0]);
        move(src.toPath(), target.toPath(), isOverride);
    }
	// 移动文件或者目录
    public static void moveContent(File src, File target, boolean isOverride) throws IORuntimeException {
        Assert.notNull(src, "Src file must be not null!", new Object[0]);
        Assert.notNull(target, "target file must be not null!", new Object[0]);
        moveContent(src.toPath(), target.toPath(), isOverride);
    }
	// 修改文件或目录的文件名,不变更路径,只是简单修改文件名,不保留扩展名。
    public static File rename(File file, String newName, boolean isOverride) {
        return rename(file, newName, false, isOverride);
    }
	// 修改文件或目录的文件名,不变更路径,只是简单修改文件名重命名有两种模式:1、isRetainExt为true时,保留原扩展名:
    public static File rename(File file, String newName, boolean isRetainExt, boolean isOverride) {
        if (isRetainExt) {
            String extName = extName(file);
            if (StrUtil.isNotBlank(extName)) {
                newName = newName.concat(".").concat(extName);
            }
        }

        return rename(file.toPath(), newName, isOverride).toFile();
    }
	// 获取规范的绝对路径
    public static String getCanonicalPath(File file) {
        if (null == file) {
            return null;
        } else {
            try {
                return file.getCanonicalPath();
            } catch (IOException var2) {
                throw new IORuntimeException(var2);
            }
        }
    }
	// 获取规范的绝对路径
    public static String getAbsolutePath(String path, Class<?> baseClass) {
        String normalPath;
        if (path == null) {
            normalPath = "";
        } else {
            normalPath = normalize(path);
            if (isAbsolutePath(normalPath)) {
                return normalPath;
            }
        }

        URL url = ResourceUtil.getResource(normalPath, baseClass);
        if (null != url) {
            return normalize(URLUtil.getDecodedPath(url));
        } else {
            String classPath = ClassUtil.getClassPath();
            return null == classPath ? path : normalize(classPath.concat((String)Objects.requireNonNull(path)));
        }
    }
	// 获取规范的绝对路径
    public static String getAbsolutePath(String path) {
        return getAbsolutePath(path, (Class)null);
    }
	// 获取规范的绝对路径
    public static String getAbsolutePath(File file) {
        if (file == null) {
            return null;
        } else {
            try {
                return file.getCanonicalPath();
            } catch (IOException var2) {
                return file.getAbsolutePath();
            }
        }
    }
	// 给定路径已经是绝对路径此方法并没有针对路径做标准化,建议先执行normalize(String)方法标准化路径后判断绝对路径判断条件是:以/开头的路径满足类似于 c:/xxxxx,其中祖意,不区分大小写满足类似于 d:\xxxxx,其中祖母随意,不区分大小写
    public static boolean isAbsolutePath(String path) {
        if (StrUtil.isEmpty(path)) {
            return false;
        } else {
            return '/' == path.charAt(0) || ReUtil.isMatch(PATTERN_PATH_ABSOLUTE, path);
        }
    }
	// 判断是否为目录,如果path为null,则返回false
    public static boolean isDirectory(String path) {
        return null != path && file(path).isDirectory();
    }
	// 判断是否为目录,如果path为null,则返回false
    public static boolean isDirectory(File file) {
        return null != file && file.isDirectory();
    }
	// 判断是否为文件,如果path为null,则返回false
    public static boolean isFile(String path) {
        return null != path && file(path).isFile();
    }
	// 判断是否为文件,如果path为null,则返回false
    public static boolean isFile(File file) {
        return null != file && file.isFile();
    }
	// 检查两个文件是否是同一个文件 所谓文件相同,是指File对象是否指向同一个文件或文件夹
    public static boolean equals(File file1, File file2) throws IORuntimeException {
        Assert.notNull(file1);
        Assert.notNull(file2);
        if (file1.exists() && file2.exists()) {
            return equals(file1.toPath(), file2.toPath());
        } else {
            return !file1.exists() && !file2.exists() && pathEquals(file1, file2);
        }
    }
	// 比较两个文件内容是否相同 首先比较长度,长度一致再比较内容
    public static boolean contentEquals(File file1, File file2) throws IORuntimeException {
        boolean file1Exists = file1.exists();
        if (file1Exists != file2.exists()) {
            return false;
        } else if (!file1Exists) {
            return true;
        } else if (!file1.isDirectory() && !file2.isDirectory()) {
            if (file1.length() != file2.length()) {
                return false;
            } else if (equals(file1, file2)) {
                return true;
            } else {
                InputStream input1 = null;
                BufferedInputStream input2 = null;

                boolean var5;
                try {
                    input1 = getInputStream(file1);
                    input2 = getInputStream(file2);
                    var5 = IoUtil.contentEquals(input1, input2);
                } finally {
                    IoUtil.close(input1);
                    IoUtil.close(input2);
                }

                return var5;
            }
        } else {
            throw new IORuntimeException("Can't compare directories, only files");
        }
    }
	// 比较两个文件内容是否相同 首先比较长度,长度一致再比较内容,比较内容采用按行读取,每行比较
    public static boolean contentEqualsIgnoreEOL(File file1, File file2, Charset charset) throws IORuntimeException {
        boolean file1Exists = file1.exists();
        if (file1Exists != file2.exists()) {
            return false;
        } else if (!file1Exists) {
            return true;
        } else if (!file1.isDirectory() && !file2.isDirectory()) {
            if (equals(file1, file2)) {
                return true;
            } else {
                Reader input1 = null;
                BufferedReader input2 = null;

                boolean var6;
                try {
                    input1 = getReader(file1, charset);
                    input2 = getReader(file2, charset);
                    var6 = IoUtil.contentEqualsIgnoreEOL(input1, input2);
                } finally {
                    IoUtil.close(input1);
                    IoUtil.close(input2);
                }

                return var6;
            }
        } else {
            throw new IORuntimeException("Can't compare directories, only files");
        }
    }
	// 文件路径是否相同 取两个文件的绝对路径比较,在Windows下忽略大小写,在Linux下不忽略。
    public static boolean pathEquals(File file1, File file2) {
        if (isWindows()) {
            try {
                if (StrUtil.equalsIgnoreCase(file1.getCanonicalPath(), file2.getCanonicalPath())) {
                    return true;
                }
            } catch (Exception var3) {
                if (StrUtil.equalsIgnoreCase(file1.getAbsolutePath(), file2.getAbsolutePath())) {
                    return true;
                }
            }
        } else {
            try {
                if (StrUtil.equals(file1.getCanonicalPath(), file2.getCanonicalPath())) {
                    return true;
                }
            } catch (Exception var4) {
                if (StrUtil.equals(file1.getAbsolutePath(), file2.getAbsolutePath())) {
                    return true;
                }
            }
        }

        return false;
    }
	// 获得最后一个文件路径分隔符的位置
    public static int lastIndexOfSeparator(String filePath) {
        if (StrUtil.isNotEmpty(filePath)) {
            int i = filePath.length();

            while(true) {
                --i;
                if (i < 0) {
                    break;
                }

                char c = filePath.charAt(i);
                if (CharUtil.isFileSeparator(c)) {
                    return i;
                }
            }
        }

        return -1;
    }

    /** @deprecated */
    @Deprecated
    public static boolean isModifed(File file, long lastModifyTime) {
        return isModified(file, lastModifyTime);
    }
	// 判断文件是否被改动 如果文件对象为 null 或者文件不存在,被视为改动
    public static boolean isModified(File file, long lastModifyTime) {
        if (null != file && file.exists()) {
            return file.lastModified() != lastModifyTime;
        } else {
            return true;
        }
    }
	// 修复路径 如果原路径尾部有分隔符,则保留为标准分隔符(/),否则不保留 1.
    public static String normalize(String path) {
        if (path == null) {
            return null;
        } else {
            String pathToUse = StrUtil.removePrefixIgnoreCase(path, "classpath:");
            pathToUse = StrUtil.removePrefixIgnoreCase(pathToUse, "file:");
            if (StrUtil.startWith(pathToUse, '~')) {
                pathToUse = getUserHomePath() + pathToUse.substring(1);
            }

            pathToUse = pathToUse.replaceAll("[/\\\\]+", "/");
            pathToUse = StrUtil.trimStart(pathToUse);
            if (path.startsWith("\\\\")) {
                pathToUse = "\\" + pathToUse;
            }

            String prefix = "";
            int prefixIndex = pathToUse.indexOf(":");
            if (prefixIndex > -1) {
                prefix = pathToUse.substring(0, prefixIndex + 1);
                if (StrUtil.startWith(prefix, '/')) {
                    prefix = prefix.substring(1);
                }

                if (!prefix.contains("/")) {
                    pathToUse = pathToUse.substring(prefixIndex + 1);
                } else {
                    prefix = "";
                }
            }

            if (pathToUse.startsWith("/")) {
                prefix = prefix + "/";
                pathToUse = pathToUse.substring(1);
            }

            List<String> pathList = StrUtil.split(pathToUse, '/');
            List<String> pathElements = new LinkedList();
            int tops = 0;

            for(int i = pathList.size() - 1; i >= 0; --i) {
                String element = (String)pathList.get(i);
                if (!".".equals(element)) {
                    if ("..".equals(element)) {
                        ++tops;
                    } else if (tops > 0) {
                        --tops;
                    } else {
                        pathElements.add(0, element);
                    }
                }
            }

            if (tops > 0 && StrUtil.isEmpty(prefix)) {
                while(tops-- > 0) {
                    pathElements.add(0, "..");
                }
            }

            return prefix + CollUtil.join(pathElements, "/");
        }
    }
	// 获得相对子路径
    public static String subPath(String rootDir, File file) {
        try {
            return subPath(rootDir, file.getCanonicalPath());
        } catch (IOException var3) {
            throw new IORuntimeException(var3);
        }
    }
	// 获得相对子路径
    public static String subPath(String dirPath, String filePath) {
        if (StrUtil.isNotEmpty(dirPath) && StrUtil.isNotEmpty(filePath)) {
            dirPath = StrUtil.removeSuffix(normalize(dirPath), "/");
            filePath = normalize(filePath);
            String result = StrUtil.removePrefixIgnoreCase(filePath, dirPath);
            return StrUtil.removePrefix(result, "/");
        } else {
            return filePath;
        }
    }
	// 返回文件名
    public static String getName(File file) {
        return FileNameUtil.getName(file);
    }
	// 返回文件名
    public static String getName(String filePath) {
        return FileNameUtil.getName(filePath);
    }
	// 获取文件后缀名,扩展名不带“.”
    public static String getSuffix(File file) {
        return FileNameUtil.getSuffix(file);
    }
	// 获取文件后缀名,扩展名不带“.”
    public static String getSuffix(String fileName) {
        return FileNameUtil.getSuffix(fileName);
    }
	// 返回主文件名
    public static String getPrefix(File file) {
        return FileNameUtil.getPrefix(file);
    }
	// 返回主文件名
    public static String getPrefix(String fileName) {
        return FileNameUtil.getPrefix(fileName);
    }
	// 返回主文件名
    public static String mainName(File file) {
        return FileNameUtil.mainName(file);
    }
	// 返回主文件名
    public static String mainName(String fileName) {
        return FileNameUtil.mainName(fileName);
    }
	// 获取文件扩展名(后缀名),扩展名不带“.”
    public static String extName(File file) {
        return FileNameUtil.extName(file);
    }
	// 获取文件扩展名(后缀名),扩展名不带“.”
    public static String extName(String fileName) {
        return FileNameUtil.extName(fileName);
    }
	// 判断文件路径是否有指定后缀,忽略大小写 常用语判断扩展名
    public static boolean pathEndsWith(File file, String suffix) {
        return file.getPath().toLowerCase().endsWith(suffix);
    }
	// 根据文件流的头部信息获得文件类型 1、无法识别类型默认按照扩展名识别2、xls、doc、msi头信息无法区分,按照扩展名区分 3、zip可能为docx、xlsx、pptx、jar、war头信息无法区分,按照扩展名区分
    public static String getType(File file) throws IORuntimeException {
        return FileTypeUtil.getType(file);
    }
	// 获得输入流
    public static BufferedInputStream getInputStream(File file) throws IORuntimeException {
        return IoUtil.toBuffered(IoUtil.toStream(file));
    }
	// 获得输入流
    public static BufferedInputStream getInputStream(String path) throws IORuntimeException {
        return getInputStream(file(path));
    }
	// 获得BOM输入流,用于处理带BOM头的文件
    public static BOMInputStream getBOMInputStream(File file) throws IORuntimeException {
        try {
            return new BOMInputStream(new FileInputStream(file));
        } catch (IOException var2) {
            throw new IORuntimeException(var2);
        }
    }
	// 读取带BOM头的文件为Reader
    public static BufferedReader getBOMReader(File file) {
        return IoUtil.getReader(getBOMInputStream(file));
    }
	// 获得一个文件读取器
    public static BufferedReader getUtf8Reader(File file) throws IORuntimeException {
        return getReader(file, CharsetUtil.CHARSET_UTF_8);
    }
	// 获得一个文件读取器
    public static BufferedReader getUtf8Reader(String path) throws IORuntimeException {
        return getReader(path, CharsetUtil.CHARSET_UTF_8);
    }

    /** @deprecated */
    @Deprecated
    public static BufferedReader getReader(File file, String charsetName) throws IORuntimeException {
        return IoUtil.getReader(getInputStream(file), CharsetUtil.charset(charsetName));
    }
	// 获得一个文件读取器
    public static BufferedReader getReader(File file, Charset charset) throws IORuntimeException {
        return IoUtil.getReader(getInputStream(file), charset);
    }

    /** @deprecated */
    @Deprecated
    public static BufferedReader getReader(String path, String charsetName) throws IORuntimeException {
        return getReader(path, CharsetUtil.charset(charsetName));
    }
	// 获得一个文件读取器
    public static BufferedReader getReader(String path, Charset charset) throws IORuntimeException {
        return getReader(file(path), charset);
    }
	// 读取文件所有数据 文件的长度不能超过Integer.MAX_VALUE
    public static byte[] readBytes(File file) throws IORuntimeException {
        return cn.hutool.core.io.file.FileReader.create(file).readBytes();
    }
	// 读取文件所有数据 文件的长度不能超过Integer.MAX_VALUE
    public static byte[] readBytes(String filePath) throws IORuntimeException {
        return readBytes(file(filePath));
    }
	// 读取文件内容
    public static String readUtf8String(File file) throws IORuntimeException {
        return readString(file, CharsetUtil.CHARSET_UTF_8);
    }
	// 读取文件内容
    public static String readUtf8String(String path) throws IORuntimeException {
        return readString(path, CharsetUtil.CHARSET_UTF_8);
    }

    /** @deprecated */
    @Deprecated
    public static String readString(File file, String charsetName) throws IORuntimeException {
        return readString(file, CharsetUtil.charset(charsetName));
    }
	// 读取文件内容
    public static String readString(File file, Charset charset) throws IORuntimeException {
        return cn.hutool.core.io.file.FileReader.create(file, charset).readString();
    }

    /** @deprecated */
    @Deprecated
    public static String readString(String path, String charsetName) throws IORuntimeException {
        return readString(path, CharsetUtil.charset(charsetName));
    }
	// 读取文件内容
    public static String readString(String path, Charset charset) throws IORuntimeException {
        return readString(file(path), charset);
    }

    /** @deprecated */
    @Deprecated
    public static String readString(URL url, String charsetName) throws IORuntimeException {
        return readString(url, CharsetUtil.charset(charsetName));
    }
	// 读取文件内容
    public static String readString(URL url, Charset charset) throws IORuntimeException {
        if (url == null) {
            throw new NullPointerException("Empty url provided!");
        } else {
            InputStream in = null;

            String var3;
            try {
                in = url.openStream();
                var3 = IoUtil.read(in, charset);
            } catch (IOException var7) {
                throw new IORuntimeException(var7);
            } finally {
                IoUtil.close(in);
            }

            return var3;
        }
    }
	// 从文件中读取每一行的UTF-8编码数据
    public static <T extends Collection<String>> T readUtf8Lines(String path, T collection) throws IORuntimeException {
        return readLines(path, CharsetUtil.CHARSET_UTF_8, collection);
    }
	// 从文件中读取每一行数据
    public static <T extends Collection<String>> T readLines(String path, String charset, T collection) throws IORuntimeException {
        return readLines(file(path), charset, collection);
    }
	// 从文件中读取每一行数据
    public static <T extends Collection<String>> T readLines(String path, Charset charset, T collection) throws IORuntimeException {
        return readLines(file(path), charset, collection);
    }
	// 从文件中读取每一行的UTF-8编码数据
    public static <T extends Collection<String>> T readUtf8Lines(File file, T collection) throws IORuntimeException {
        return readLines(file, CharsetUtil.CHARSET_UTF_8, collection);
    }
	// 从文件中读取每一行数据
    public static <T extends Collection<String>> T readLines(File file, String charset, T collection) throws IORuntimeException {
        return cn.hutool.core.io.file.FileReader.create(file, CharsetUtil.charset(charset)).readLines(collection);
    }
	// 从文件中读取每一行数据
    public static <T extends Collection<String>> T readLines(File file, Charset charset, T collection) throws IORuntimeException {
        return cn.hutool.core.io.file.FileReader.create(file, charset).readLines(collection);
    }
	// 从文件中读取每一行的UTF-8编码数据
    public static <T extends Collection<String>> T readUtf8Lines(URL url, T collection) throws IORuntimeException {
        return readLines(url, CharsetUtil.CHARSET_UTF_8, collection);
    }

    /** @deprecated */
    @Deprecated
    public static <T extends Collection<String>> T readLines(URL url, String charsetName, T collection) throws IORuntimeException {
        return readLines(url, CharsetUtil.charset(charsetName), collection);
    }
	// 从文件中读取每一行数据
    public static <T extends Collection<String>> T readLines(URL url, Charset charset, T collection) throws IORuntimeException {
        InputStream in = null;

        Collection var4;
        try {
            in = url.openStream();
            var4 = IoUtil.readLines(in, charset, collection);
        } catch (IOException var8) {
            throw new IORuntimeException(var8);
        } finally {
            IoUtil.close(in);
        }

        return var4;
    }
	// 从文件中读取每一行的UTF-8编码数据
    public static List<String> readUtf8Lines(URL url) throws IORuntimeException {
        return readLines(url, CharsetUtil.CHARSET_UTF_8);
    }

    /** @deprecated */
    @Deprecated
    public static List<String> readLines(URL url, String charsetName) throws IORuntimeException {
        return readLines(url, CharsetUtil.charset(charsetName));
    }

    public static List<String> readLines(URL url, Charset charset) throws IORuntimeException {
        return (List)readLines((URL)url, (Charset)charset, (Collection)(new ArrayList()));
    }

    public static List<String> readUtf8Lines(String path) throws IORuntimeException {
        return readLines(path, CharsetUtil.CHARSET_UTF_8);
    }

    public static List<String> readLines(String path, String charset) throws IORuntimeException {
        return (List)readLines((String)path, (String)charset, (Collection)(new ArrayList()));
    }

    public static List<String> readLines(String path, Charset charset) throws IORuntimeException {
        return (List)readLines((String)path, (Charset)charset, (Collection)(new ArrayList()));
    }

    public static List<String> readUtf8Lines(File file) throws IORuntimeException {
        return readLines(file, CharsetUtil.CHARSET_UTF_8);
    }

    public static List<String> readLines(File file, String charset) throws IORuntimeException {
        return (List)readLines((File)file, (String)charset, (Collection)(new ArrayList()));
    }

    public static List<String> readLines(File file, Charset charset) throws IORuntimeException {
        return (List)readLines((File)file, (Charset)charset, (Collection)(new ArrayList()));
    }

    public static void readUtf8Lines(File file, LineHandler lineHandler) throws IORuntimeException {
        readLines(file, CharsetUtil.CHARSET_UTF_8, lineHandler);
    }

    public static void readLines(File file, Charset charset, LineHandler lineHandler) throws IORuntimeException {
        cn.hutool.core.io.file.FileReader.create(file, charset).readLines(lineHandler);
    }

    public static void readLines(RandomAccessFile file, Charset charset, LineHandler lineHandler) {
        try {
            String line;
            while((line = file.readLine()) != null) {
                lineHandler.handle(CharsetUtil.convert(line, CharsetUtil.CHARSET_ISO_8859_1, charset));
            }

        } catch (IOException var5) {
            throw new IORuntimeException(var5);
        }
    }

    public static void readLine(RandomAccessFile file, Charset charset, LineHandler lineHandler) {
        String line = readLine(file, charset);
        if (null != line) {
            lineHandler.handle(line);
        }

    }

    public static String readLine(RandomAccessFile file, Charset charset) {
        String line;
        try {
            line = file.readLine();
        } catch (IOException var4) {
            throw new IORuntimeException(var4);
        }

        return null != line ? CharsetUtil.convert(line, CharsetUtil.CHARSET_ISO_8859_1, charset) : null;
    }
	// 按照给定的readerHandler读取文件中的数据
    public static <T> T loadUtf8(String path, ReaderHandler<T> readerHandler) throws IORuntimeException {
        return load(path, CharsetUtil.CHARSET_UTF_8, readerHandler);
    }
	// 按照给定的readerHandler读取文件中的数据
    public static <T> T load(String path, String charset, ReaderHandler<T> readerHandler) throws IORuntimeException {
        return cn.hutool.core.io.file.FileReader.create(file(path), CharsetUtil.charset(charset)).read(readerHandler);
    }

    public static <T> T load(String path, Charset charset, ReaderHandler<T> readerHandler) throws IORuntimeException {
        return cn.hutool.core.io.file.FileReader.create(file(path), charset).read(readerHandler);
    }

    public static <T> T loadUtf8(File file, ReaderHandler<T> readerHandler) throws IORuntimeException {
        return load(file, CharsetUtil.CHARSET_UTF_8, readerHandler);
    }

    public static <T> T load(File file, Charset charset, ReaderHandler<T> readerHandler) throws IORuntimeException {
        return cn.hutool.core.io.file.FileReader.create(file, charset).read(readerHandler);
    }
	// 获得一个输出流对象
    public static BufferedOutputStream getOutputStream(File file) throws IORuntimeException {
        FileOutputStream out;
        try {
            out = new FileOutputStream(touch(file));
        } catch (IOException var3) {
            throw new IORuntimeException(var3);
        }

        return IoUtil.toBuffered(out);
    }

    public static BufferedOutputStream getOutputStream(String path) throws IORuntimeException {
        return getOutputStream(touch(path));
    }

    /** @deprecated */
    @Deprecated
    public static BufferedWriter getWriter(String path, String charsetName, boolean isAppend) throws IORuntimeException {
        return getWriter(path, Charset.forName(charsetName), isAppend);
    }
	// 获得一个带缓存的写入对象
    public static BufferedWriter getWriter(String path, Charset charset, boolean isAppend) throws IORuntimeException {
        return getWriter(touch(path), charset, isAppend);
    }

    /** @deprecated */
    @Deprecated
    public static BufferedWriter getWriter(File file, String charsetName, boolean isAppend) throws IORuntimeException {
        return getWriter(file, Charset.forName(charsetName), isAppend);
    }

    public static BufferedWriter getWriter(File file, Charset charset, boolean isAppend) throws IORuntimeException {
        return FileWriter.create(file, charset).getWriter(isAppend);
    }
	// 获得一个打印写入对象,可以有print
    public static PrintWriter getPrintWriter(String path, String charset, boolean isAppend) throws IORuntimeException {
        return new PrintWriter(getWriter(path, charset, isAppend));
    }

    public static PrintWriter getPrintWriter(String path, Charset charset, boolean isAppend) throws IORuntimeException {
        return new PrintWriter(getWriter(path, charset, isAppend));
    }

    public static PrintWriter getPrintWriter(File file, String charset, boolean isAppend) throws IORuntimeException {
        return new PrintWriter(getWriter(file, charset, isAppend));
    }

    public static PrintWriter getPrintWriter(File file, Charset charset, boolean isAppend) throws IORuntimeException {
        return new PrintWriter(getWriter(file, charset, isAppend));
    }
	// 获取当前系统的换行分隔符
    public static String getLineSeparator() {
        return System.lineSeparator();
    }
	// 将String写入文件,覆盖模式,字符集为UTF-8
    public static File writeUtf8String(String content, String path) throws IORuntimeException {
        return writeString(content, path, CharsetUtil.CHARSET_UTF_8);
    }
	// 将String写入文件,覆盖模式,字符集为UTF-8
    public static File writeUtf8String(String content, File file) throws IORuntimeException {
        return writeString(content, file, CharsetUtil.CHARSET_UTF_8);
    }
	// 将String写入文件,覆盖模式
    public static File writeString(String content, String path, String charset) throws IORuntimeException {
        return writeString(content, touch(path), charset);
    }

    public static File writeString(String content, String path, Charset charset) throws IORuntimeException {
        return writeString(content, touch(path), charset);
    }

    public static File writeString(String content, File file, String charset) throws IORuntimeException {
        return FileWriter.create(file, CharsetUtil.charset(charset)).write(content);
    }

    public static File writeString(String content, File file, Charset charset) throws IORuntimeException {
        return FileWriter.create(file, charset).write(content);
    }
	// 将String写入文件,UTF-8编码追加模式
    public static File appendUtf8String(String content, String path) throws IORuntimeException {
        return appendString(content, path, CharsetUtil.CHARSET_UTF_8);
    }
	// 将String写入文件,追加模式	
    public static File appendString(String content, String path, String charset) throws IORuntimeException {
        return appendString(content, touch(path), charset);
    }
	// 将String写入文件,追加模式	
    public static File appendString(String content, String path, Charset charset) throws IORuntimeException {
        return appendString(content, touch(path), charset);
    }

    public static File appendUtf8String(String content, File file) throws IORuntimeException {
        return appendString(content, file, CharsetUtil.CHARSET_UTF_8);
    }

    public static File appendString(String content, File file, String charset) throws IORuntimeException {
        return FileWriter.create(file, CharsetUtil.charset(charset)).append(content);
    }

    public static File appendString(String content, File file, Charset charset) throws IORuntimeException {
        return FileWriter.create(file, charset).append(content);
    }

    public static <T> File writeUtf8Lines(Collection<T> list, String path) throws IORuntimeException {
        return writeLines(list, path, CharsetUtil.CHARSET_UTF_8);
    }

    public static <T> File writeUtf8Lines(Collection<T> list, File file) throws IORuntimeException {
        return writeLines(list, file, CharsetUtil.CHARSET_UTF_8);
    }

    public static <T> File writeLines(Collection<T> list, String path, String charset) throws IORuntimeException {
        return writeLines(list, path, charset, false);
    }

    public static <T> File writeLines(Collection<T> list, String path, Charset charset) throws IORuntimeException {
        return writeLines(list, path, charset, false);
    }

    public static <T> File writeLines(Collection<T> list, File file, String charset) throws IORuntimeException {
        return writeLines(list, file, charset, false);
    }

    public static <T> File writeLines(Collection<T> list, File file, Charset charset) throws IORuntimeException {
        return writeLines(list, file, charset, false);
    }

    public static <T> File appendUtf8Lines(Collection<T> list, File file) throws IORuntimeException {
        return appendLines(list, file, CharsetUtil.CHARSET_UTF_8);
    }

    public static <T> File appendUtf8Lines(Collection<T> list, String path) throws IORuntimeException {
        return appendLines(list, path, CharsetUtil.CHARSET_UTF_8);
    }

    public static <T> File appendLines(Collection<T> list, String path, String charset) throws IORuntimeException {
        return writeLines(list, path, charset, true);
    }

    public static <T> File appendLines(Collection<T> list, File file, String charset) throws IORuntimeException {
        return writeLines(list, file, charset, true);
    }

    public static <T> File appendLines(Collection<T> list, String path, Charset charset) throws IORuntimeException {
        return writeLines(list, path, charset, true);
    }

    public static <T> File appendLines(Collection<T> list, File file, Charset charset) throws IORuntimeException {
        return writeLines(list, file, charset, true);
    }

    public static <T> File writeLines(Collection<T> list, String path, String charset, boolean isAppend) throws IORuntimeException {
        return writeLines(list, file(path), charset, isAppend);
    }

    public static <T> File writeLines(Collection<T> list, String path, Charset charset, boolean isAppend) throws IORuntimeException {
        return writeLines(list, file(path), charset, isAppend);
    }

    public static <T> File writeLines(Collection<T> list, File file, String charset, boolean isAppend) throws IORuntimeException {
        return FileWriter.create(file, CharsetUtil.charset(charset)).writeLines(list, isAppend);
    }

    public static <T> File writeLines(Collection<T> list, File file, Charset charset, boolean isAppend) throws IORuntimeException {
        return FileWriter.create(file, charset).writeLines(list, isAppend);
    }

    public static File writeUtf8Map(Map<?, ?> map, File file, String kvSeparator, boolean isAppend) throws IORuntimeException {
        return FileWriter.create(file, CharsetUtil.CHARSET_UTF_8).writeMap(map, kvSeparator, isAppend);
    }

    public static File writeMap(Map<?, ?> map, File file, Charset charset, String kvSeparator, boolean isAppend) throws IORuntimeException {
        return FileWriter.create(file, charset).writeMap(map, kvSeparator, isAppend);
    }

    public static File writeBytes(byte[] data, String path) throws IORuntimeException {
        return writeBytes(data, touch(path));
    }

    public static File writeBytes(byte[] data, File dest) throws IORuntimeException {
        return writeBytes(data, dest, 0, data.length, false);
    }

    public static File writeBytes(byte[] data, File dest, int off, int len, boolean isAppend) throws IORuntimeException {
        return FileWriter.create(dest).write(data, off, len, isAppend);
    }

    public static File writeFromStream(InputStream in, File dest) throws IORuntimeException {
        return writeFromStream(in, dest, true);
    }

    public static File writeFromStream(InputStream in, File dest, boolean isCloseIn) throws IORuntimeException {
        return FileWriter.create(dest).writeFromStream(in, isCloseIn);
    }

    public static File writeFromStream(InputStream in, String fullFilePath) throws IORuntimeException {
        return writeFromStream(in, touch(fullFilePath));
    }
	// 将文件写入流中,此方法不会关闭输出流
    public static long writeToStream(File file, OutputStream out) throws IORuntimeException {
        return cn.hutool.core.io.file.FileReader.create(file).writeToStream(out);
    }
	// 将路径对应文件写入流中,此方法不会关闭输出流
    public static long writeToStream(String fullFilePath, OutputStream out) throws IORuntimeException {
        return writeToStream(touch(fullFilePath), out);
    }
	// 可读的文件大小
    public static String readableFileSize(File file) {
        return readableFileSize(file.length());
    }
	// 可读的文件大小
    public static String readableFileSize(long size) {
        return DataSizeUtil.format(size);
    }
	// 转换文件编码 此方法用于转换文件编码,读取的文件实际编码必须与指定的srcCharset编码一致,否则导致乱码
    public static File convertCharset(File file, Charset srcCharset, Charset destCharset) {
        return CharsetUtil.convert(file, srcCharset, destCharset);
    }
	// 转换换行符 将给定文件的换行符转换为指定换行符
    public static File convertLineSeparator(File file, Charset charset, LineSeparator lineSeparator) {
        List<String> lines = readLines(file, charset);
        return FileWriter.create(file, charset).writeLines(lines, lineSeparator, false);
    }
	// 文件名中是否包含在Windows下不支持的非法字符,包括: \ / : * ? " < > |
    public static String cleanInvalid(String fileName) {
        return FileNameUtil.cleanInvalid(fileName);
    }
	// 计算文件CRC32校验码
    public static boolean containsInvalid(String fileName) {
        return FileNameUtil.containsInvalid(fileName);
    }
	// 计算文件CRC32校验码
    public static long checksumCRC32(File file) throws IORuntimeException {
        return checksum(file, new CRC32()).getValue();
    }
	// 计算文件校验码
    public static Checksum checksum(File file, Checksum checksum) throws IORuntimeException {
        Assert.notNull(file, "File is null !", new Object[0]);
        if (file.isDirectory()) {
            throw new IllegalArgumentException("Checksums can't be computed on directories");
        } else {
            try {
                return IoUtil.checksum(new FileInputStream(file), checksum);
            } catch (FileNotFoundException var3) {
                throw new IORuntimeException(var3);
            }
        }
    }
	// 获取Web项目下的web root路径 原理是首先获取ClassPath路径,由于在web项目中ClassPath位于 WEB-INF/classes/下,故向上获取两级目录即可。
    public static File getWebRoot() {
        String classPath = ClassUtil.getClassPath();
        return StrUtil.isNotBlank(classPath) ? getParent((File)file(classPath), 2) : null;
    }
	// 获取指定层级的父路径
    public static String getParent(String filePath, int level) {
        File parent = getParent(file(filePath), level);

        try {
            return null == parent ? null : parent.getCanonicalPath();
        } catch (IOException var4) {
            throw new IORuntimeException(var4);
        }
    }
	// 获取指定层级的父路径
    public static File getParent(File file, int level) {
        if (level >= 1 && null != file) {
            File parentFile;
            try {
                parentFile = file.getCanonicalFile().getParentFile();
            } catch (IOException var4) {
                throw new IORuntimeException(var4);
            }

            return 1 == level ? parentFile : getParent(parentFile, level - 1);
        } else {
            return file;
        }
    }
	// 检查父完整路径是否为自路径的前半部分,如果不是说明不是子路径,可能存在slip注入。
    public static File checkSlip(File parentFile, File file) throws IllegalArgumentException {
        if (null != parentFile && null != file) {
            String parentCanonicalPath;
            String canonicalPath;
            try {
                parentCanonicalPath = parentFile.getCanonicalPath();
                canonicalPath = file.getCanonicalPath();
            } catch (IOException var5) {
                parentCanonicalPath = parentFile.getAbsolutePath();
                canonicalPath = file.getAbsolutePath();
            }

            if (!canonicalPath.startsWith(parentCanonicalPath)) {
                throw new IllegalArgumentException("New file is outside of the parent dir: " + file.getName());
            }
        }

        return file;
    }
	// 根据文件扩展名获得MimeType
    public static String getMimeType(String filePath) {
        String contentType = URLConnection.getFileNameMap().getContentTypeFor(filePath);
        if (null == contentType) {
            if (StrUtil.endWithIgnoreCase(filePath, ".css")) {
                contentType = "text/css";
            } else if (StrUtil.endWithIgnoreCase(filePath, ".js")) {
                contentType = "application/x-javascript";
            } else if (StrUtil.endWithIgnoreCase(filePath, ".rar")) {
                contentType = "application/x-rar-compressed";
            } else if (StrUtil.endWithIgnoreCase(filePath, ".7z")) {
                contentType = "application/x-7z-compressed";
            } else if (StrUtil.endWithIgnoreCase(filePath, ".wgt")) {
                contentType = "application/widget";
            }
        }

        if (null == contentType) {
            contentType = getMimeType(Paths.get(filePath));
        }

        return contentType;
    }
	// 判断是否为符号链接文件
    public static boolean isSymlink(File file) {
        return isSymlink(file.toPath());
    }
	// 判断给定的目录是否为给定文件或文件夹的子目录
    public static boolean isSub(File parent, File sub) {
        Assert.notNull(parent);
        Assert.notNull(sub);
        return isSub(parent.toPath(), sub.toPath());
    }
	// 创建RandomAccessFile
    public static RandomAccessFile createRandomAccessFile(Path path, FileMode mode) {
        return createRandomAccessFile(path.toFile(), mode);
    }

    public static RandomAccessFile createRandomAccessFile(File file, FileMode mode) {
        try {
            return new RandomAccessFile(file, mode.name());
        } catch (FileNotFoundException var3) {
            throw new IORuntimeException(var3);
        }
    }
	// 文件内容跟随器,实现类似Linux下"tail -f"命令功能 此方法会阻塞当前线程
    public static void tail(File file, LineHandler handler) {
        tail(file, CharsetUtil.CHARSET_UTF_8, handler);
    }

    public static void tail(File file, Charset charset, LineHandler handler) {
        (new Tailer(file, charset, handler)).start();
    }

    public static void tail(File file, Charset charset) {
        tail(file, charset, Tailer.CONSOLE_HANDLER);
    }

    private static File buildFile(File outFile, String fileName) {
        fileName = fileName.replace('\\', '/');
        if (!isWindows() && fileName.lastIndexOf(47, fileName.length() - 2) > 0) {
            List<String> pathParts = StrUtil.split(fileName, '/', false, true);
            int lastPartIndex = pathParts.size() - 1;

            for(int i = 0; i < lastPartIndex; ++i) {
                outFile = new File(outFile, (String)pathParts.get(i));
            }

            outFile.mkdirs();
            fileName = (String)pathParts.get(lastPartIndex);
        }

        return new File(outFile, fileName);
    }

    static {
        FILE_SEPARATOR = File.separator;
        PATH_SEPARATOR = File.pathSeparator;
        PATTERN_PATH_ABSOLUTE = Pattern.compile("^[a-zA-Z]:([/\\\\].*)?");
    }
}

文件类型判断

在文件上传时,有时候我们需要判断文件类型。但是又不能简单的通过扩展名来判断(防止恶意脚本等通过上传到服务器上),于是我们需要在服务端通过读取文件的首部几个二进制位来判断常用的文件类型。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package cn.hutool.core.io;

import cn.hutool.core.util.StrUtil;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentSkipListMap;

public class FileTypeUtil {
    private static final Map<String, String> FILE_TYPE_MAP = new ConcurrentSkipListMap((s1, s2) -> {
        int len1 = s1.length();
        int len2 = s2.length();
        return len1 == len2 ? s1.compareTo(s2) : len2 - len1;
    });

    public FileTypeUtil() {
    }
	// 增加文件类型映射 如果已经存在将覆盖之前的映射
    public static String putFileType(String fileStreamHexHead, String extName) {
        return (String)FILE_TYPE_MAP.put(fileStreamHexHead, extName);
    }
	// 移除文件类型映射
    public static String removeFileType(String fileStreamHexHead) {
        return (String)FILE_TYPE_MAP.remove(fileStreamHexHead);
    }
	// 根据文件流的头部信息获得文件类型
    public static String getType(String fileStreamHexHead) {
        Iterator var1 = FILE_TYPE_MAP.entrySet().iterator();

        Entry fileTypeEntry;
        do {
            if (!var1.hasNext()) {
                return null;
            }

            fileTypeEntry = (Entry)var1.next();
        } while(!StrUtil.startWithIgnoreCase(fileStreamHexHead, (CharSequence)fileTypeEntry.getKey()));

        return (String)fileTypeEntry.getValue();
    }
	// 根据文件流的头部信息获得文件类型 注意此方法会读取头部28个bytes,造成此流接下来读取时缺少部分bytes 因此如果想服用此流,流需支持InputStream.reset()方法。
    public static String getType(InputStream in) throws IORuntimeException {
        return getType(IoUtil.readHex28Upper(in));
    }
	// 根据文件流的头部信息获得文件类型 注意此方法会读取头部28个bytes,造成此流接下来读取时缺少部分bytes 因此如果想服用此流,流需支持InputStream.reset()方法。
    public static String getType(InputStream in, String filename) {
        String typeName = getType(in);
        if (null == typeName) {
            typeName = FileUtil.extName(filename);
        } else {
            String extName;
            if ("xls".equals(typeName)) {
                extName = FileUtil.extName(filename);
                if ("doc".equalsIgnoreCase(extName)) {
                    typeName = "doc";
                } else if ("msi".equalsIgnoreCase(extName)) {
                    typeName = "msi";
                } else if ("ppt".equalsIgnoreCase(extName)) {
                    typeName = "ppt";
                }
            } else if ("zip".equals(typeName)) {
                extName = FileUtil.extName(filename);
                if ("docx".equalsIgnoreCase(extName)) {
                    typeName = "docx";
                } else if ("xlsx".equalsIgnoreCase(extName)) {
                    typeName = "xlsx";
                } else if ("pptx".equalsIgnoreCase(extName)) {
                    typeName = "pptx";
                } else if ("jar".equalsIgnoreCase(extName)) {
                    typeName = "jar";
                } else if ("war".equalsIgnoreCase(extName)) {
                    typeName = "war";
                } else if ("ofd".equalsIgnoreCase(extName)) {
                    typeName = "ofd";
                } else if ("apk".equalsIgnoreCase(extName)) {
                    typeName = "apk";
                }
            } else if ("jar".equals(typeName)) {
                extName = FileUtil.extName(filename);
                if ("xlsx".equalsIgnoreCase(extName)) {
                    typeName = "xlsx";
                } else if ("docx".equalsIgnoreCase(extName)) {
                    typeName = "docx";
                } else if ("pptx".equalsIgnoreCase(extName)) {
                    typeName = "pptx";
                } else if ("zip".equalsIgnoreCase(extName)) {
                    typeName = "zip";
                } else if ("apk".equalsIgnoreCase(extName)) {
                    typeName = "apk";
                }
            }
        }

        return typeName;
    }
	// 根据文件流的头部信息获得文件类型
    public static String getType(File file) throws IORuntimeException {
        FileInputStream in = null;

        String var2;
        try {
            in = IoUtil.toStream(file);
            var2 = getType(in, file.getName());
        } finally {
            IoUtil.close(in);
        }

        return var2;
    }
	// 通过路径获得文件类型
    public static String getTypeByPath(String path) throws IORuntimeException {
        return getType(FileUtil.file(path));
    }

    static {
        FILE_TYPE_MAP.put("ffd8ff", "jpg");
        FILE_TYPE_MAP.put("89504e47", "png");
        FILE_TYPE_MAP.put("4749463837", "gif");
        FILE_TYPE_MAP.put("4749463839", "gif");
        FILE_TYPE_MAP.put("49492a00227105008037", "tif");
        FILE_TYPE_MAP.put("424d", "bmp");
        FILE_TYPE_MAP.put("41433130313500000000", "dwg");
        FILE_TYPE_MAP.put("7b5c727466315c616e73", "rtf");
        FILE_TYPE_MAP.put("38425053000100000000", "psd");
        FILE_TYPE_MAP.put("46726f6d3a203d3f6762", "eml");
        FILE_TYPE_MAP.put("5374616E64617264204A", "mdb");
        FILE_TYPE_MAP.put("252150532D41646F6265", "ps");
        FILE_TYPE_MAP.put("255044462d312e", "pdf");
        FILE_TYPE_MAP.put("2e524d46000000120001", "rmvb");
        FILE_TYPE_MAP.put("464c5601050000000900", "flv");
        FILE_TYPE_MAP.put("0000001C66747970", "mp4");
        FILE_TYPE_MAP.put("00000020667479706", "mp4");
        FILE_TYPE_MAP.put("00000018667479706D70", "mp4");
        FILE_TYPE_MAP.put("49443303000000002176", "mp3");
        FILE_TYPE_MAP.put("000001ba210001000180", "mpg");
        FILE_TYPE_MAP.put("3026b2758e66cf11a6d9", "wmv");
        FILE_TYPE_MAP.put("52494646e27807005741", "wav");
        FILE_TYPE_MAP.put("52494646d07d60074156", "avi");
        FILE_TYPE_MAP.put("4d546864000000060001", "mid");
        FILE_TYPE_MAP.put("526172211a0700cf9073", "rar");
        FILE_TYPE_MAP.put("235468697320636f6e66", "ini");
        FILE_TYPE_MAP.put("504B03040a0000000000", "jar");
        FILE_TYPE_MAP.put("504B0304140008000800", "jar");
        FILE_TYPE_MAP.put("d0cf11e0a1b11ae10", "xls");
        FILE_TYPE_MAP.put("504B0304", "zip");
        FILE_TYPE_MAP.put("4d5a9000030000000400", "exe");
        FILE_TYPE_MAP.put("3c25402070616765206c", "jsp");
        FILE_TYPE_MAP.put("4d616e69666573742d56", "mf");
        FILE_TYPE_MAP.put("7061636b616765207765", "java");
        FILE_TYPE_MAP.put("406563686f206f66660d", "bat");
        FILE_TYPE_MAP.put("1f8b0800000000000000", "gz");
        FILE_TYPE_MAP.put("cafebabe0000002e0041", "class");
        FILE_TYPE_MAP.put("49545346030000006000", "chm");
        FILE_TYPE_MAP.put("04000000010000001300", "mxp");
        FILE_TYPE_MAP.put("6431303a637265617465", "torrent");
        FILE_TYPE_MAP.put("6D6F6F76", "mov");
        FILE_TYPE_MAP.put("FF575043", "wpd");
        FILE_TYPE_MAP.put("CFAD12FEC5FD746F", "dbx");
        FILE_TYPE_MAP.put("2142444E", "pst");
        FILE_TYPE_MAP.put("AC9EBD8F", "qdf");
        FILE_TYPE_MAP.put("E3828596", "pwl");
        FILE_TYPE_MAP.put("2E7261FD", "ram");
        FILE_TYPE_MAP.put("52494646", "webp");
    }
}

文件监听

监听一个文件的变化或者目录的变动,包括文件的创建、修改、删除,以及目录下文件的创建、修改和删除

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package cn.hutool.core.io.watch;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent.Kind;

public class WatchMonitor extends WatchServer {
    private static final long serialVersionUID = 1L;
    // 事件丢失
    public static final Kind<?> OVERFLOW;
    // 修改事件
    public static final Kind<?> ENTRY_MODIFY;
    // 创建事件
    public static final Kind<?> ENTRY_CREATE;
    // 删除事件
    public static final Kind<?> ENTRY_DELETE;
    // 所有事件
    public static final Kind<?>[] EVENTS_ALL;
    private Path path;
    // 设置监听目录的最大深入,目录层级大于制定层级的变更将不被监听,默认只监听当前层级目录
    private int maxDepth;
    private Path filePath;
    private Watcher watcher;
	// 创建并初始化监听
    public static WatchMonitor create(URL url, Kind<?>... events) {
        return create((URL)url, 0, events);
    }
	// 创建并初始化监听
    public static WatchMonitor create(URL url, int maxDepth, Kind<?>... events) {
        return create(URLUtil.toURI(url), maxDepth, events);
    }

    public static WatchMonitor create(URI uri, Kind<?>... events) {
        return create((URI)uri, 0, events);
    }

    public static WatchMonitor create(URI uri, int maxDepth, Kind<?>... events) {
        return create(Paths.get(uri), maxDepth, events);
    }

    public static WatchMonitor create(File file, Kind<?>... events) {
        return create((File)file, 0, events);
    }

    public static WatchMonitor create(File file, int maxDepth, Kind<?>... events) {
        return create(file.toPath(), maxDepth, events);
    }

    public static WatchMonitor create(String path, Kind<?>... events) {
        return create((String)path, 0, events);
    }

    public static WatchMonitor create(String path, int maxDepth, Kind<?>... events) {
        return create(Paths.get(path), maxDepth, events);
    }

    public static WatchMonitor create(Path path, Kind<?>... events) {
        return create((Path)path, 0, events);
    }

    public static WatchMonitor create(Path path, int maxDepth, Kind<?>... events) {
        return new WatchMonitor(path, maxDepth, events);
    }
	// 创建并初始化监听,监听所有事件
    public static WatchMonitor createAll(URI uri, Watcher watcher) {
        return createAll(Paths.get(uri), watcher);
    }

    public static WatchMonitor createAll(URL url, Watcher watcher) {
        try {
            return createAll(Paths.get(url.toURI()), watcher);
        } catch (URISyntaxException var3) {
            throw new WatchException(var3);
        }
    }

    public static WatchMonitor createAll(File file, Watcher watcher) {
        return createAll(file.toPath(), watcher);
    }

    public static WatchMonitor createAll(String path, Watcher watcher) {
        return createAll(Paths.get(path), watcher);
    }

    public static WatchMonitor createAll(Path path, Watcher watcher) {
        WatchMonitor watchMonitor = create(path, EVENTS_ALL);
        watchMonitor.setWatcher(watcher);
        return watchMonitor;
    }

    public WatchMonitor(File file, Kind<?>... events) {
        this(file.toPath(), events);
    }

    public WatchMonitor(String path, Kind<?>... events) {
        this(Paths.get(path), events);
    }

    public WatchMonitor(Path path, Kind<?>... events) {
        this(path, 0, events);
    }

    public WatchMonitor(Path path, int maxDepth, Kind<?>... events) {
        this.path = path;
        this.maxDepth = maxDepth;
        this.events = events;
        this.init();
    }
	// 初始化
    public void init() throws WatchException {
        if (!Files.exists(this.path, new LinkOption[]{LinkOption.NOFOLLOW_LINKS})) {
            Path lastPathEle = FileUtil.getLastPathEle(this.path);
            if (null != lastPathEle) {
                String lastPathEleStr = lastPathEle.toString();
                if (StrUtil.contains(lastPathEleStr, '.') && !StrUtil.endWithIgnoreCase(lastPathEleStr, ".d")) {
                    this.filePath = this.path;
                    this.path = this.filePath.getParent();
                }
            }

            try {
                Files.createDirectories(this.path);
            } catch (IOException var3) {
                throw new IORuntimeException(var3);
            }
        } else if (Files.isRegularFile(this.path, new LinkOption[]{LinkOption.NOFOLLOW_LINKS})) {
            this.filePath = this.path;
            this.path = this.filePath.getParent();
        }

        super.init();
    }
	// 设置监听 多个监听请使用WatcherChain
    public WatchMonitor setWatcher(Watcher watcher) {
        this.watcher = watcher;
        return this;
    }

    public void run() {
        this.watch();
    }
	// 开始监听事件,阻塞当前进程
    public void watch() {
        this.watch(this.watcher);
    }

    public void watch(Watcher watcher) throws WatchException {
        if (this.isClosed) {
            throw new WatchException("Watch Monitor is closed !");
        } else {
            this.registerPath();

            while(!this.isClosed) {
                this.doTakeAndWatch(watcher);
            }

        }
    }
	// 当监听目录时,监听目录的最大深度 当设置值为1(或小于1)时,表示不递归监听子目录
    public WatchMonitor setMaxDepth(int maxDepth) {
        this.maxDepth = maxDepth;
        return this;
    }

    private void doTakeAndWatch(Watcher watcher) {
        super.watch(watcher, (watchEvent) -> {
            return null == this.filePath || this.filePath.endsWith(watchEvent.context().toString());
        });
    }

    private void registerPath() {
        this.registerPath(this.path, null != this.filePath ? 0 : this.maxDepth);
    }

    static {
        OVERFLOW = WatchKind.OVERFLOW.getValue();
        ENTRY_MODIFY = WatchKind.MODIFY.getValue();
        ENTRY_CREATE = WatchKind.CREATE.getValue();
        ENTRY_DELETE = WatchKind.DELETE.getValue();
        EVENTS_ALL = WatchKind.ALL;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听不见你的名字

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值