package api.common.util;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.springframework.util.ResourceUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

@Slf4j
public class FileUtil {

    /**
     * 拷贝文件。
     */
    @SneakyThrows
    public static void cp(String sourceFile, String targetFile) {
        createParentDirectory(targetFile);
        FileInputStream fileInputStream = new FileInputStream(sourceFile);
        FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
        FileChannel readChannel = fileInputStream.getChannel();
        FileChannel writeChannel = fileOutputStream.getChannel();
        //自动生成与文件大小相同但不超过 8096 的缓存，即缓存最大为 8096。
        writeChannel.transferFrom(readChannel, 0, readChannel.size());
        writeChannel.close();
        readChannel.close();
        fileOutputStream.close();
        fileInputStream.close();
    }

    public static void cpR(String sourceDirectoryPath, String targetDirectoryPath) {
        try {
            File[] files = new File(sourceDirectoryPath).listFiles();
            for (File file : files) {
                if (file.isDirectory()) {
                    file.mkdir();
                }
                if (file.isFile()) {
                    String sourceFilePath = file.getAbsolutePath();
                    String targetFilePath = sourceFilePath.replace(sourceDirectoryPath, targetDirectoryPath);
                    cp(sourceFilePath, targetFilePath);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获得绝对路径
     *
     * @param parentDirectoryPath 父目录
     * @param subStringOfFileName 文件名的子串
     * @return 绝对路径
     */
    @SneakyThrows
    public static String getAbsolutePath(String parentDirectoryPath, String subStringOfFileName) {
        File parentDirectoryFile = new File(parentDirectoryPath);
        if (!parentDirectoryFile.exists()) {
            throw new RuntimeException("FileUtil.getAbsolutePath() -- 目录 " + parentDirectoryPath + " 不存在！");
        }
        File[] files = parentDirectoryFile.listFiles();
        if (files == null || files.length == 0) {
            throw new RuntimeException("FileUtil.getAbsolutePath() -- 目录 " + parentDirectoryPath + " 下没有文件！");

        }
        for (File file : files) {
            if (file.getName().contains(subStringOfFileName)) {
                return file.getAbsolutePath();
            }
        }
        throw new RuntimeException("FileUtil.getAbsolutePath() -- 目录 " + parentDirectoryPath + " 下的文件名包含" + subStringOfFileName + " 的文件不存在！");
    }

    /**
     * 获取所有同一类型的文件列表。
     *
     * @param rootPath 文件 File 路径。
     * @param suffix   文件后缀类型。
     */
    @SneakyThrows
    public static List<String> listAbsolutePathByFiletype(String rootPath, String suffix) {
        List<String> result = new LinkedList<>();
        File rootFile = new File(rootPath);
        //1 判断文件是否存在
        if (!rootFile.exists()) {
            throw new RuntimeException("FileUtil--listAbsolutePathByFiletype 文件 " + rootPath + " 不存在！");
        }
        //2 判断是否是文件
        if (rootFile.isFile() && rootFile.getName().equals(suffix)) {
            return new ArrayList<>(Collections.singletonList(rootPath));
        }
        //3 判断是否是目录
        if (rootFile.isDirectory()) {
            File[] children = rootFile.listFiles();
            if (children != null) {
                for (File child : children) {
                    result.addAll(listAbsolutePathByFiletype(child.getAbsolutePath(), suffix));
                }
            }
        }
        return result;
    }


    /**
     * 获取当前目录下的一级文件全路径列表
     *
     * @param path 根路径
     * @return 文件全路径列表
     */
    public static List<String> ls(String path) {
        File[] files = new File(path).listFiles();
        if (files == null) {
            throw new RuntimeException("路径不存在！");
        }
        List<String> result = new ArrayList<>();
        for (File file : files) {
            result.add(file.getAbsolutePath());
        }
        return result;
    }


    @SneakyThrows
    public static String read(String path) {
        return read(getFile(path));
    }

    @SneakyThrows
    public static String cat(String path) {
        return read(getFile(path));
    }

    @SneakyThrows
    public static String read(File file) {
        return read(Files.newInputStream(file.toPath()));
    }

    public static String read(InputStream inputStream) throws IOException {
        StringBuilder result = new StringBuilder();
        byte[] buf = new byte[4096];//创建字节数组，存储临时读取的数据
        int len;//记录数据读取的长度
        //循环读取数据
        while ((len = inputStream.read(buf)) != -1) { //长度为-1则读取完毕
            result.append(new String(buf, 0, len)).append("\n");
        }
        inputStream.close();
        return result.toString();
    }


    // -------------------------------- A --------------------------------

    /**
     * 添加文件后缀
     *
     * @param url 路径
     * @return 有后缀名的文件
     */
    public static String addSeparator(String url) {
        if (File.separator.equals(url.charAt(url.length() - 1) + "")) {
            return url;
        }
        return url + File.separator;
    }

    /**
     * 添加文件后缀
     *
     * @param ambiguousFileName 不确定是否有后缀的文件名
     * @return 有后缀名的文件
     */
    public static String addSuffix(String ambiguousFileName, String suffix) {
        if (ambiguousFileName == null || "".equals(ambiguousFileName)) {
            throw new RuntimeException("文件名参数为空！");
        }
        String[] split = ambiguousFileName.split("\\.");
        return split[0] + suffix;
    }


    // -------------------------------- B --------------------------------
    // -------------------------------- C --------------------------------

    /**
     * 检查文件
     * 0 代表不存在
     * 1 代表是文件
     * 2 代表是目录
     *
     * @param file 文件
     * @return 是否正确
     */
    public static String checkFile(File file) {
        if (!file.exists()) {
            return "0";
        }
        if (file.isFile()) {
            return "1";
        }
        if (file.isDirectory()) {
            return "2";
        }
        return null;
    }


    /**
     * 检查文件后缀
     *
     * @param file   文件
     * @param suffix 文件后缀
     * @return 是否正确
     */
    public static boolean checkFileType(File file, String suffix) {
        String fileName = file.getName();
        int fileNameLength = fileName.length();
        int suffixLength = suffix.length();
        if (fileNameLength < suffixLength) {
            return false;
        }
        return suffix.equals(fileName.substring(fileNameLength - suffixLength));
    }


    /**
     * 自适应转换文件大小的单位
     *
     * @param dataSize long 型的文件大小
     * @return 带单位的 String 类型文件大小
     */
    public static String convertUnit(long dataSize) {
        String dataSizeStr = null;
        if (dataSize >= 0 && dataSize < 1024) {
            dataSizeStr = dataSize + "B";
        } else if (dataSize >= 1024 && dataSize < 1024 * 1024) {
            dataSizeStr = dataSize / 1024 + "KB";
        } else if (dataSize >= 1024 * 1024 && dataSize < 1024 * 1024 * 1024) {
            dataSizeStr = dataSize / 1024 / 1024 + "MB";
        } else if (dataSize >= 1024 * 1024 * 1024 && dataSize < 1024 * 1024 * 1024 * 1024L) {
            dataSizeStr = dataSize / 1024 / 1024 / 1024 + "GB";
        } else if (dataSize >= 1024 * 1024 * 1024 * 1024L) {
            dataSizeStr = dataSize / 1024 / 1024 / 1024 / 1024 + "TB";
        }
        return dataSizeStr;
    }

    /**
     * 计算目录包含的文件数量
     *
     * @param directory 目录的 File 对象
     * @return 文件数量
     */
    public static int count(File directory) {
        return Objects.requireNonNull(directory.listFiles()).length;
    }

    /**
     * 计算目录包含的指定类型的文件数量
     *
     * @param directory 目录的 File 对象
     * @param type      文件类型，不带点，例如 txt
     * @return 文件数量
     */
    @SneakyThrows
    public static long countByType(String directory, String type) {
        String[] list = new File(directory).list();
        return Arrays.stream(list).filter(absolutePath -> absolutePath.endsWith(type)).count();
    }

    /**
     * 计算目录包含的文件数量
     *
     * @param directoryPath 目录的路径
     * @return 文件数量
     */
    public static int count(String directoryPath) throws Exception {
        return count(getDirectory(directoryPath));
    }

    /**
     * 复制文件
     *
     * @param source 原始文件路径
     * @param target 目标路径
     * @throws Exception 异常
     */
    public static void copy(String source, String target) throws Exception {
        File targetFile = new File(target);
        boolean delete = true;
        if (targetFile.exists()) {
            delete = targetFile.delete();
        }
        if (delete) {
            createParentDirectory(target);
            Files.copy(getFile(source).toPath(), new File(target).toPath());
        } else {
            throw new Exception("删除 " + target + " 失败！");
        }

    }

    /**
     * 创建目录
     */
    public static boolean createDirectory(String path) {
        boolean mkdir = false;
        File file = new File(path);
        if (!file.exists()) {
            mkdir = file.mkdirs();
        }
        return mkdir;
    }


    /**
     * 创建目录
     */
    public static boolean mkdir(String path) {
        boolean mkdir = false;
        File file = new File(path);
        if (!file.exists()) {
            mkdir = file.mkdirs();
        }
        return mkdir;
    }


    /**
     * 创建父目录
     */
    public static boolean createParentDirectory(String path) {
        boolean mkdir = false;
        File file = new File(path);
        if (!file.getParentFile().exists()) {
            mkdir = file.getParentFile().mkdirs();
        }
        return mkdir;
    }

    /**
     * 创建父目录
     */
    public static boolean createParentDirectory(File file) {
        boolean mkdir = true;
        if (!file.getParentFile().exists()) {
            mkdir = file.getParentFile().mkdirs();
        }
        return mkdir;
    }

    /**
     * 创建文件。
     */
    public static boolean createFile(String path) throws IOException {
        File file = new File(path);
        boolean result = createParentDirectory(file);
        if (!file.exists()) {
            result = file.createNewFile();
        }
        return result;
    }

    /**
     * 创建文件。
     */
    public static boolean createFile(File file) throws IOException {
        boolean result = createParentDirectory(file);
        if (!file.exists()) {
            result = file.createNewFile();
        }
        return result;
    }


    /**
     * 拷贝文件。
     */
    public static void copyFile(String readFrom, String writeTo) throws IOException {
        createParentDirectory(writeTo);
        FileInputStream fileInputStream = new FileInputStream(readFrom);
        FileOutputStream fileOutputStream = new FileOutputStream(writeTo);
        FileChannel readChannel = fileInputStream.getChannel();
        FileChannel writeChannel = fileOutputStream.getChannel();
        //自动生成与文件大小相同但不超过 8096 的缓存，即缓存最大为 8096。
        writeChannel.transferFrom(readChannel, 0, readChannel.size());
        writeChannel.close();
        readChannel.close();
        fileOutputStream.close();
        fileInputStream.close();
    }


    public static void copyBytes(InputStream in, OutputStream out, int bufferSize) throws IOException {
        byte[] buffer = new byte[bufferSize];
        for (int bytesRead = in.read(buffer); bytesRead >= 0; bytesRead = in.read(buffer)) {
            out.write(buffer, 0, bytesRead);
        }
    }


    // -------------------------------- D --------------------------------


    /**
     * 供 http 下载使用
     *
     * @param fileName   文件名
     * @param in         输入流
     * @param response   响应对象
     * @param bufferSize 缓存大小
     * @throws IOException 异常
     */
    public static void downloadForHttp(String fileName, InputStream in, HttpServletResponse response, int bufferSize) throws IOException {
        response.setContentType("application/octet-stream;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
        copyBytes(in, response.getOutputStream(), bufferSize);
    }

    public static Set<String> decompress(InputStream inputStream, String targetDirectoryPath, String fileType) throws IOException {
        Set<String> fileList = new HashSet<>();
        if (!targetDirectoryPath.endsWith("/")) {
            targetDirectoryPath += "/";
        }
        if (".tar".equals(fileType)) {
            TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(inputStream);
            TarArchiveEntry entry;
            // 将 tar 文件解压到 extractPath 目录下
            while ((entry = tarArchiveInputStream.getNextTarEntry()) != null) {
                if (entry.isDirectory()) {
                    continue;
                }
                createDirectory(targetDirectoryPath);
                File currentFile = new File(targetDirectoryPath + entry.getName());
                File parent = currentFile.getParentFile();
                if (!parent.exists()) {
                    boolean mkdirs = parent.mkdirs();
                }
                fileList.add(currentFile.getAbsolutePath());
                // 将文件写出到解压的目录
                copyBytes(tarArchiveInputStream, new FileOutputStream(currentFile), 4096);
            }


        }
        if (".tar.gz".equals(fileType) || ".tgz".equals(fileType) || ".gz".equals(fileType)) {
            TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream((new GzipCompressorInputStream(inputStream)));
            TarArchiveEntry entry;
            // 将 tar 文件解压到 extractPath 目录下
            while ((entry = tarArchiveInputStream.getNextTarEntry()) != null) {
                if (entry.isDirectory()) {
                    continue;
                }
                createDirectory(targetDirectoryPath);
                File currentFile = new File(targetDirectoryPath + entry.getName());
                File parent = currentFile.getParentFile();
                if (!parent.exists()) {
                    boolean mkdirs = parent.mkdirs();
                }
                fileList.add(currentFile.getAbsolutePath());
                // 将文件写出到解压的目录
                copyBytes(tarArchiveInputStream, new FileOutputStream(currentFile), 4096);
            }


        }
        inputStream.close();
        return fileList;
    }


    /**
     * 扫描压缩包中是否存在指定文件
     *
     * @param inputStream
     * @param fileType
     * @return
     * @throws IOException
     */
    public static boolean scan(InputStream inputStream, String fileType, String targetFileName) throws IOException {
        if (".tar".equals(fileType)) {
            TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(inputStream);
            TarArchiveEntry entry;
            // 将 tar 文件解压到 extractPath 目录下
            while ((entry = tarArchiveInputStream.getNextTarEntry()) != null) {
                if (entry.getName().contains(targetFileName)) {
                    return true;
                }
            }
        }
        if (".tar.gz".equals(fileType) || ".tgz".equals(fileType) || ".gz".equals(fileType)) {
            TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream((new GzipCompressorInputStream(inputStream)));
            TarArchiveEntry entry;
            // 将 tar 文件解压到 extractPath 目录下
            while ((entry = tarArchiveInputStream.getNextTarEntry()) != null) {
                if (entry.getName().contains(targetFileName)) {
                    return true;
                }
            }
        }
        inputStream.close();
        return false;
    }

    // -------------------------------- F --------------------------------
    // -------------------------------- G --------------------------------

    /**
     * 获得去除文件后缀的文件名
     */
    public static List<String> getAbsolutePathList(File[] files) {
        List<String> absolutePathList = new ArrayList<>();
        for (File file : files) {
            absolutePathList.add(file.getAbsolutePath());
        }
        return absolutePathList;
    }

    /**
     * 获得目录的 File 对象
     */
    public static File getDirectory(String path) throws Exception {
        // 获取文件
        File file = new File(path);
        if (!file.exists()) {
            throw new Exception("文件" + path + "不存在！");
        }
        if (!file.isDirectory()) {
            throw new Exception("文件" + path + "不是目录！");
        }
        return file;
    }

    /**
     * 获得去除文件后缀的文件名
     */
    public static File getFile(String path) throws Exception {
        // 获取文件
        File file = new File(path);
        if (!file.exists()) {
            throw new Exception("文件" + path + "不存在！");
        }
        return file;
    }

    /**
     * 获得去除文件后缀的文件名
     */
    public static String getFilename(File file) {
        return file.getName().split("\\.")[0];
    }

    /**
     * 获得去除文件后缀的文件名
     */
    public static String getFilename(String path) throws Exception {
        return getFilename(getFile(path));
    }

    /**
     * 获得去除文件后缀的文件名
     */
    public static List<String> getFilenameList(File[] files) {
        List<String> fileNameList = new ArrayList<>();
        for (File file : files) {
            fileNameList.add(file.getName());
        }
        return fileNameList;
    }

    /**
     * 获取文件行数
     *
     * @param path 文件路径
     * @return 文件行数
     * @throws IOException 异常
     */
    public static long getFileLineNum(String path) throws IOException {
        return Files.lines(Paths.get(path)).count();
    }

    /**
     * 获取文件名称
     *
     * @param path 文件路径
     * @return 文件名
     */
    public static String getFileName(String path) {
        return new File(path).getName();
    }

    /**
     * 获取 classpath 文件
     *
     * @param relativePath 相对路径
     */
    public static File getResourceFile(String relativePath) throws IOException {
        return ResourceUtils.getFile("classpath:" + relativePath);
    }

    /**
     * 获取所有文件列表。
     *
     * @param root 文件 File 对象。
     */
    public static List<File> list(File root) throws Exception {

        List<File> result = new LinkedList<>();
        //1 判断文件是否存在
        if (!root.exists()) {
            throw new Exception("文件" + root.getAbsolutePath() + "不存在！");
        }
        //2 判断是否是文件
        if (root.isFile()) {
            return new ArrayList<>(Collections.singletonList(root));
        }
        //3 判断是否是目录
        if (root.isDirectory()) {
            File[] children = root.listFiles();
            if (children != null) {
                for (File child : children) {
                    result.addAll(list(child));
                }
            }
        }
        return result;
    }

    /**
     * 获取所有文件列表。
     *
     * @param rootPath 文件 File 对象。
     */
    public static List<String> list(String rootPath) throws Exception {

        List<String> result = new ArrayList<>();
        File root = new File(rootPath);
        //1 判断文件是否存在
        if (!root.exists()) {
            throw new Exception("文件" + root.getAbsolutePath() + "不存在！");
        }
        //2 判断是否是文件
        if (root.isFile()) {
            return new ArrayList<>(Collections.singletonList(rootPath));
        }
        //3 判断是否是目录
        if (root.isDirectory()) {
            File[] children = root.listFiles();
            if (children != null) {
                for (File child : children) {
                    result.addAll(list(child.getAbsolutePath()));
                }
            }
        }
        return result;
    }

    public static void deleteFileBySubstring(String parentDirectoryPath, String substringOfFilename) {
        File[] files = new File(parentDirectoryPath).listFiles();
        if (files == null || files.length == 0) {
            return;
        }
        for (File file : files) {
            if (file.getName().contains(substringOfFilename)) {
                boolean delete = file.delete();
            }
        }
    }

    /**
     * 获取所有文件列表。
     *
     * @param rootPath 文件 File 对象。
     */
    public static List<String> listAbsolutePath(String rootPath) throws Exception {

        List<String> result = new ArrayList<>();
        File root = new File(rootPath);
        //1 判断文件是否存在
        if (!root.exists()) {
            throw new Exception("文件" + root.getAbsolutePath() + "不存在！");
        }
        //2 判断是否是文件
        if (root.isFile()) {
            return new ArrayList<>(Collections.singletonList(rootPath));
        }
        //3 判断是否是目录
        if (root.isDirectory()) {
            File[] children = root.listFiles();
            if (children != null) {
                for (File child : children) {
                    result.addAll(list(child.getAbsolutePath()));
                }
            }
        }
        return result;
    }

    public static String getFilenameWithoutSuffix(String absolutePath) {
        String[] split0 = absolutePath.split("/");
        String[] split1 = split0[split0.length - 1].split("\\.");
        return split1[0];
    }

    /**
     * 获取所有同一类型的文件列表。
     *
     * @param rootPath 文件 File 路径。
     * @param type     文件类型不带点，如 txt。
     */
    @SneakyThrows
    public static List<String> listAbsolutePathByTypeAndLength(String rootPath, String type, int length) {
        List<String> result = new LinkedList<>();
        File rootFile = new File(rootPath);
        //1 判断文件是否存在
        if (!rootFile.exists()) {
            throw new RuntimeException("文件 " + rootPath + " 不存在！");
        }
        //2 判断是否是文件
        if (rootFile.isFile() && rootFile.getName().endsWith(type) && rootFile.getName().length() == length) {
            return new ArrayList<>(Collections.singletonList(rootPath));
        }
        //3 判断是否是目录
        if (rootFile.isDirectory()) {
            File[] children = rootFile.listFiles();
            if (children != null) {
                for (File child : children) {
                    result.addAll(listAbsolutePathByTypeAndLength(child.getAbsolutePath(), type, length));
                }
            }
        }
        return result;
    }

    /**
     * 获取所有同一类型的文件列表。
     *
     * @param root   文件 File 对象。
     * @param suffix 文件后缀类型。
     */
    public static List<File> listMatchSuffix(File root, String suffix) throws Exception {
        List<File> result = new LinkedList<>();
        //1 判断文件是否存在
        if (!root.exists()) {
            throw new Exception("文件" + root.getAbsolutePath() + "不存在！");
        }
        //2 判断是否是文件
        if (root.isFile() && checkFileType(root, suffix)) {
            return new ArrayList<>(Collections.singletonList(root));
        }
        //3 判断是否是目录
        if (root.isDirectory()) {
            File[] children = root.listFiles();
            if (children != null) {
                for (File child : children) {
                    result.addAll(listMatchSuffix(child, suffix));
                }
            }
        }
        return result;
    }


    /**
     * 获取所有同一名称的文件列表。
     *
     * @param root         文件 File 对象。
     * @param fullFilename 带后缀的文件名。
     */
    public static List<File> listMatchName(File root, String fullFilename) throws Exception {
        List<File> result = new LinkedList<>();
        //1 判断文件是否存在
        if (!root.exists()) {
            throw new Exception("文件" + root.getAbsolutePath() + "不存在！");
        }
        //2 判断是否是文件且文件名是否一致
        if (root.isFile() && fullFilename.equals(root.getName())) {
            return new ArrayList<>(Collections.singletonList(root));
        }
        //3 判断是否是目录
        if (root.isDirectory()) {
            File[] children = root.listFiles();
            if (children != null) {
                for (File child : children) {
                    result.addAll(listMatchName(child, fullFilename));
                }
            }
        }
        return result;
    }


    /**
     * 获得父级文件。
     *
     * @return 父级文件
     */
    public static File getParentFile(String path) throws Exception {
        return getParentFile(getFile(path));
    }

    /**
     * 获得父级文件。
     *
     * @return 父级文件
     */
    public static File getParentFile(File file) {
        return file.getParentFile();
    }

    /**
     * 获得父级目录名。
     */
    public static String getParentDirectoryName(File file) {
        return getParentFile(file).getName().split("\\.")[0];
    }

    /**
     * 获得父级目录名。
     */
    public static String getParentDirectoryName(String path) throws Exception {
        return getParentDirectoryName(getFile(path));
    }

    /**
     * 获取路径下的所有文件总大小
     */
    public static double getSize(File file) throws Exception {
        //判断文件是否存在
        if (file.exists()) {
            //如果是目录则递归计算其内容的总大小
            if (file.isDirectory()) {
                File[] children = file.listFiles();
                if (children == null) {
                    return 0;
                }
                double size = 0;
                for (File f : children)
                    size += getSize(f);
                return size;
            } else {//如果是文件则直接返回其大小,以“兆”为单位
                return (double) file.length() / 1024 / 1024;
            }
        } else {
            throw new Exception("文件或者文件夹不存在，请检查路径" + file.getAbsolutePath() + "是否正确！");
        }
    }


    public static String getSuffix(String path) {
        String[] split = path.split("\\.");
        return split[split.length - 1];
    }

    /**
     * 计算文件大小，递归算法
     */
    public static long getTotalSizeOfFilesInDir(final File file) throws Exception {
        //判断文件是否存在
        if (file.exists()) {
            if (file.isFile()) {
                return file.length();
            }
            final File[] children = file.listFiles();
            long total = 0;
            if (children != null) {
                for (final File child : children) {
                    total += getTotalSizeOfFilesInDir(child);
                }
            }
            return total;
        } else {
            throw new Exception("文件或者文件夹不存在，请检查路径" + file.getAbsolutePath() + "是否正确！");
        }
    }


    // -------------------------------- H --------------------------------
    // -------------------------------- I --------------------------------


    // -------------------------------- J --------------------------------
    // -------------------------------- K --------------------------------
    // -------------------------------- L --------------------------------
    // -------------------------------- M --------------------------------


    /**
     * 修改文件。
     */
    public static void modifyFile(String path, int offset, byte[] bytes) throws IOException {
        createFile(path);
        RandomAccessFile randomAccessFile = new RandomAccessFile(path, "rw");
        FileChannel channel = randomAccessFile.getChannel();
        MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, offset, bytes.length);
        mappedByteBuffer.put(bytes);
        channel.close();
    }

    /**
     * 移动文件或者重命名文件
     *
     * @param source 原始文件路径
     * @param target 目标路径
     * @return 是否操作成功
     * @throws Exception 异常
     */
    public static boolean mv(String source, String target) throws Exception {
        return getFile(source).renameTo(new File(target));
    }


    // -------------------------------- N --------------------------------
    // -------------------------------- O --------------------------------
    // -------------------------------- P --------------------------------
    // -------------------------------- Q --------------------------------
    // -------------------------------- R --------------------------------

    public static String readFile(String path) throws Exception {
        return readFile(getFile(path));
    }

    public static String readFile(File file) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
        StringBuilder stringBuilder = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }
        reader.close();
        return String.valueOf(stringBuilder);
    }

    public static String readInputStream(InputStream inputStream) throws Exception {
        StringBuilder result = new StringBuilder();
        byte[] buf = new byte[1024];//创建字节数组，存储临时读取的数据
        int len = 0;//记录数据读取的长度
        //循环读取数据
        while ((len = inputStream.read(buf)) != -1) { //长度为-1则读取完毕
            result.append(new String(buf, 0, len));
        }
        inputStream.close();
        return result.toString();
    }

    /**
     * 删除文件或递归删除目录
     * 递归删除目录下的所有文件及子目录下所有文件
     */
    public static boolean rm(File file) {
        if (!file.exists()) {
            return false;
        } else {
            if (file.isDirectory()) {
                String[] children = file.list();
                if (children != null) {
                    for (String child : children) {
                        if (!rm(new File(file, child))) {
                            return false;
                        }
                    }
                }
                return true;
            } else {
                return file.delete();
            }
        }
    }

    /**
     * 删除文件或递归删除目录
     * 递归删除目录下的所有文件及子目录下所有文件
     */
    public static boolean rm(String path) {
        return rm(new File(path));
    }


    public static void deleteFolder(String folderPath) {
        File folder = new File(folderPath);
        if (folder.exists()) {
            File[] files = folder.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteFolder(file.getPath());
                    } else {
                        file.delete();
                    }
                }
            }
            folder.delete();
        }
    }


    // -------------------------------- S --------------------------------

//    public static String splicingPath(String... paths) {
//        StringBuilder result = new StringBuilder();
//        for (int i = 0; i < paths.length; i++) {
//            if (i == 0) {
//                if ("/".equals(paths[0].charAt(paths.length - 1) + "")) {
//                    String substring = paths[0].substring(0, paths[0].length() - 2);
//                    result.append(substring);
//                } else {
//                    result.append(paths[0]);
//                }
//            } else {
//                String[] split = paths[i].split("/");
//
//                for (String s : split) {
//                    if (s != null && !"".equals(s)) {
//                        result.append("/").append(s);
//                    }
//                }
//            }
//        }
//        return result.toString();
//    }
    // -------------------------------- T --------------------------------
    // -------------------------------- U --------------------------------
    // -------------------------------- V --------------------------------
    // -------------------------------- W --------------------------------

    /**
     * 将字符串保存为本地文件
     */
    @SneakyThrows
    public static void writeStringToLocalFile(String string, String filePath) {
        writeInputStreamToLocalFile(new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8)), filePath);
    }

    /**
     * 将字符串保存为本地文件
     */
    @SneakyThrows
    public static void writeStringToLocalFile(String string, String filePath, int bufferLength) {
        writeInputStreamToLocalFile(new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8)), filePath, bufferLength);
    }


    /**
     * 将输入流保存为本地文件
     */
    public static void writeInputStreamToLocalFile(InputStream inputStream, String filePath) {
        writeInputStreamToLocalFile(inputStream, filePath, 4096);
    }

    /**
     * 将输入流保存为本地文件
     */
    public static void writeInputStreamToLocalFile(InputStream inputStream, String filePath, int bufferLength) {
        try {
            final File file1 = new File(filePath);
            if (file1.exists()) {
                file1.delete();
            }
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
            byte[] data = new byte[bufferLength];
            int dataLength;
            File file = new File(filePath);
            createParentDirectory(file);
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file, false));
            while ((dataLength = bufferedInputStream.read(data)) != -1) {
                bufferedOutputStream.write(data, 0, dataLength);
            }
            bufferedOutputStream.close();
            bufferedInputStream.close();
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 将输入流保存为本地文件
     */
    public static void writeInputStreamToLocalDirectory(InputStream inputStream, String directoryPath, String filename) throws IOException {
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
        byte[] data = new byte[1024];
        int dataLength;
        File file = new File(directoryPath + File.separator + filename);
        createDirectory(directoryPath);
        createFile(file);
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file, false));
        while ((dataLength = bufferedInputStream.read(data)) != -1) {
            bufferedOutputStream.write(data, 0, dataLength);
        }
        bufferedOutputStream.close();
        bufferedInputStream.close();
    }


}
