package com.pcloud.common.utils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.pcloud.common.constant.AliyunConstant;
import com.pcloud.common.constant.FilePathConst;
import com.pcloud.common.constant.OSConstant;
import com.pcloud.common.entity.UploadResultInfo;
import com.pcloud.common.exceptions.BizException;
import com.pcloud.common.exceptions.FileException;
import com.pcloud.common.utils.aliyun.OssUtils;
import com.pcloud.common.utils.encode.CpdetectorEncoding;
import com.pcloud.common.utils.string.StringTools;
import com.pcloud.common.utils.string.StringUtil;

import it.sauronsoftware.jave.AudioAttributes;
import it.sauronsoftware.jave.Encoder;
import it.sauronsoftware.jave.EncoderException;
import it.sauronsoftware.jave.EncodingAttributes;
import it.sauronsoftware.jave.InputFormatException;

/**
 * @描述：文件工具类. @作者：DiSeng.H
 * @创建时间：2016年3月10日,上午11:56:26 @版本：1.0
 */
public class FileUtils {

    /**
     * logger
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(FileUtils.class);

    /**
     * PDF
     */
    public final static String PDF = "PDF";

    /**
     * XML
     */
    public final static String XML = "XML";

    /**
     * IMAGE
     */
    public final static String IMAGE = "IMAGE";

    /**
     * 音频
     */
    public final static String AUDIO = "AUDIO";

    /**
     * 视频格式
     */
    public final static String VIDEO = "VIDEO";

    /**
     * 压缩格式
     */
    public final static String COMPRESS = "COMPRESS";

    /**
     * 办公软件
     */
    public final static String EDITOR = "EDITOR";

    /**
     * 文本格式
     */
    public final static String TEXT = "TEXT";

    /**
     * PDF文件格式
     */
    public static final String[] PDF_GATHER = {"PDF"};

    /**
     * XML文件格式
     */
    public static final String[] XML_GATHER = {"XML"};

    /**
     * 图片文件格式
     */
    public static final String[] IMAGE_GATHER = {"BMP", "JPG", "GIF", "PNG", "PCX", "TIF", "JPEG", "SVG"};

    /**
     * 音頻文件格式 【"LRC" 为歌词字幕（这里作为音頻文件上传）】
     */
    public static final String[] AUDIO_GATHER = {"AMR", "MP3", "WMA", "WAV", "AAC", "APE", "M4A"};

    /**
     * 視頻文件格式 【"SRT", "SSA", "SMI", "SUB" 为视频“外挂型”字幕（这里作为视频文件上传）】
     */
    public static final String[] VIDEO_GATHER = {"MPG", "MPEG", "AVI", "MOV", "ASF", "WMV", "NAVI", "3GP", "MKV",
            "FLV", "F4V", "RMVB", "RM", "MP4", "SRT", "SSA", "SMI", "SUB", "ASX", "M3U8"};

    /**
     * 压缩包文件格式
     */
    public static final String[] COMPRESS_GATHER = {"RAR", "ZIP"};

    /**
     * 文件格式
     */
    public static final String[] TEXT_GATHER = {"TXT", "LRC"};

    /**
     * 编辑器格式
     */
    public static final String[] EDITOR_GATHER = {"DOC", "DOCX", "XLS", "XLSX", "PPT", "PPTX"};

    /**
     * word格式
     */
    public static final String[] WORD_GATHER = {"DOC", "DOCX"};

    /**
     * excel格式
     */
    public static final String[] EXCEL_GATHER = {"XLS", "XLSX", "XLSM", "XLT", "XLTX", "XLTM", "CSV"};

    /**
     * PPT格式
     */
    public static final String[] PPT_GATHER = {"PPT", "PPTX"};

    /**
     * 根据文件类型获取文件分类
     *
     * @param fileType
     * @return
     */
    public static String getGatherName(String fileType) {
        if (StringUtil.isEmpty(fileType)) {
            return null;
        }
        // IMAGE
        if (Arrays.asList(IMAGE_GATHER).contains(fileType.toUpperCase())) {
            return IMAGE;
        }
        // AUDIO
        if (Arrays.asList(AUDIO_GATHER).contains(fileType.toUpperCase())) {
            return AUDIO;
        }
        // VIDEO
        if (Arrays.asList(VIDEO_GATHER).contains(fileType.toUpperCase())) {
            return VIDEO;
        }
        // PDF
        if (Arrays.asList(PDF_GATHER).contains(fileType.toUpperCase())) {
            return PDF;
        }
        // XML
        if (Arrays.asList(XML_GATHER).contains(fileType.toUpperCase())) {
            return XML;
        }
        // COMPRESS
        if (Arrays.asList(COMPRESS_GATHER).contains(fileType.toUpperCase())) {
            return COMPRESS;
        }
        // EDITOR
        if (Arrays.asList(EDITOR_GATHER).contains(fileType.toUpperCase())) {
            return EDITOR;
        }
        // COMPRESS
        if (Arrays.asList(TEXT_GATHER).contains(fileType.toUpperCase())) {
            return TEXT;
        }
        return null;
    }

    /**
     * 传入文件夹路径，该方法能够实现创建整个路径
     *
     * @param path 文件夹路径，不包含文件名称及后缀名
     */
    public static boolean isDir(String filePath) {
        if (StringUtil.isEmpty(filePath)) {
            return false;
        }
        String[] paths = filePath.split("/");
        String temp = "";
        for (int i = 0; i < paths.length; i++) {
            if (i == 0) {
                temp = paths[0];
            } else {
                temp += "/" + paths[i];
            }
            creatDir(temp);
        }
        return true;
    }

    /**
     * 该方法用来判断文件是否存在,如果不存在则创建
     *
     * @param filePath
     */
    public static boolean creatFile(String filePath) {
        if (StringUtil.isEmpty(filePath)) {
            return false;
        }
        File file = new File(filePath);
        if (!file.exists()) {
            try {
                file.createNewFile();
                return true;
            } catch (Exception e) {
                LOGGER.error("【文件工具】创建文件失败,<ERROR>:" + e.getMessage(), e);
            }
        }
        return false;
    }

    /**
     * 该方法用来判断文件是否存在,如果不存在则创建,该方法能够实现创建整个路径
     *
     * @param filePath
     */
    public static boolean creatFiles(String filePath) {
        LOGGER.info("【文件API】创建文件,同时创建整个路径.<START>.[filePath]=" + filePath);
        if (StringUtil.isEmpty(filePath)) {
            return false;
        }
        filePath = filePath.replace(File.separator, "/");
        String dirPath = filePath.substring(0, filePath.lastIndexOf("/"));
        File dirFile = new File(dirPath);
        if (!dirFile.exists()) {
            try {
                dirFile.mkdirs();
            } catch (Exception e) {
                LOGGER.error("【文件工具】创建文件夹失败,<ERROR>:" + e.getMessage(), e);
                return false;
            }
        }
        File file = new File(filePath);
        if (!file.exists()) {
            try {
                file.createNewFile();
                return true;
            } catch (Exception e) {
                LOGGER.error("【文件工具】创建文件失败,<ERROR>:" + e.getMessage(), e);
            }
        }
        LOGGER.info("【文件API】创建文件,同时创建整个路径.<END>");
        return false;
    }

    /**
     * 该方法用来判断文件夹是否存在，如果不存在则创建，存在则什么都不做
     *
     * @param filePath
     */
    public static boolean creatDir(String filePath) {
        if (StringUtil.isEmpty(filePath)) {
            return false;
        }
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdir();
            return true;
        }
        return false;
    }

    /**
     * 该方法用来判断文件夹是否存在，如果不存在则创建，存在则什么都不做 该方法能够实现创建整个路径
     *
     * @param filePath
     */
    public static boolean creatDirs(String filePath) {
        if (StringUtil.isEmpty(filePath)) {
            return false;
        }
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
            return true;
        }
        return false;
    }

    /**
     * 获取文件类型
     *
     * @param filePath
     */
    public static String getFileType(String filePath) {
        if (StringUtil.isEmpty(filePath)) {
            return null;
        }
        int index1 = filePath.lastIndexOf(".");
        if (index1 < 1) {
            return null;
        }
        int index2 = filePath.lastIndexOf("?");
        if (index2 > index1) {
            filePath = filePath.substring(0, filePath.lastIndexOf("?"));
        }
        filePath = filePath.replace("/", OSConstant.SEPARATOR);
        int index3 = filePath.lastIndexOf(OSConstant.SEPARATOR);
        if (index3 > index1) {
            return null;
        }
        return filePath.substring(filePath.lastIndexOf(".") + 1, filePath.length()).toLowerCase();
    }

    /**
     * 获取文件名称
     *
     * @param filePath
     */
    public static String getFileName(String filePath) {
        if (StringUtil.isEmpty(filePath)) {
            return null;
        }
        String fileName = null;
        filePath = filePath.replace("/", OSConstant.SEPARATOR);
        int index1 = filePath.lastIndexOf(".");
        if (index1 > -1) {
            int index2 = filePath.lastIndexOf("?");
            if (index2 > index1) {
                filePath = filePath.substring(0, filePath.lastIndexOf("?"));
            }
            int index3 = filePath.lastIndexOf(OSConstant.SEPARATOR);
            if (index3 > index1) {
                fileName = filePath.substring(filePath.lastIndexOf(OSConstant.SEPARATOR) + 1);
            } else {
                fileName = filePath.substring(filePath.lastIndexOf(OSConstant.SEPARATOR) + 1,
                        filePath.lastIndexOf("."));
            }
        } else {
            fileName = filePath.substring(filePath.lastIndexOf(OSConstant.SEPARATOR) + 1);
        }
        return formatName(fileName);
    }

    /**
     * 获取文件名称包括后缀
     *
     * @param filePath
     * @return
     */
    public static String getFileNameAll(String filePath) {
        if (StringUtil.isEmpty(filePath)) {
            return null;
        }
        String fileName = getFileName(filePath);
        if (StringUtil.isEmpty(fileName)) {
            return null;
        }
        String fileType = getFileType(filePath);
        return StringUtil.isEmpty(fileType) ? fileName : fileName + "." + fileType;
    }

    /**
     * 格式化文件名称，过滤特殊字符(名称太长进行截断)
     *
     * @param fileName
     */
    public static String formatName(String fileName) {
        if (StringUtil.isEmpty(fileName)) {
            return null;
        }
        try {
            String regEx = "[*/\\\\:?\"<|>\\s+%#&=.()]";
            Pattern p = Pattern.compile(regEx);
            Matcher m = p.matcher(fileName);
            String result = m.replaceAll("").trim();
            // 文件名称过长的话,限制40个字
            return result.length() > 40 ? result.substring(0, 40) : result;
        } catch (Exception e) {
            LOGGER.error("【文件API】格式化文件名称.[formatName]:" + e.getMessage(), e);
            return UUIDUitl.taskName();
        }
    }

    /**
     * 获取文件所在的目录
     *
     * @param filePath
     * @return
     */
    public static String getFileFolder(String filePath) {
        if (StringUtil.isEmpty(filePath)) {
            return null;
        }
        return filePath.substring(0, filePath.lastIndexOf(OSConstant.SEPARATOR));
    }

	/**
	 * 获取http文件路径的域名
	 * 
	 * @param fileUrl
	 * @return
	 */
	public static String getDomainName(String fileUrl) {
		if (StringUtil.isEmpty(fileUrl)) {
			return null;
		}
		Pattern p = Pattern.compile("(?<=//|)((\\w)+\\.)+\\w+");
		Matcher m = p.matcher(fileUrl);
		if (m.find()) {
			return m.group();
		}
		return null;
	}

    /**
     * FileUtils工具 File @throws
     */
    public static File copyURLToFile(String url, String dir) throws Exception {
        try {
            URL httpurl = new URL(url);
            File f = new File(dir);
            org.apache.commons.io.FileUtils.copyURLToFile(httpurl, f);
            return f;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据路径删除指定的目录或文件，无论存在与否
     *
     * @param sPath 要删除的目录或文件
     * @return 删除成功返回 true，否则返回 false。
     */
    public static boolean deleteFolder(String sPath) {
        if (StringUtil.isEmpty(sPath)) {
            return false;
        }
        boolean flag = false;
        File file = new File(sPath);
        // 判断目录或文件是否存在
        if (!file.exists()) { // 不存在返回 false
            return flag;
        } else {
            // 判断是否为文件
            if (file.isFile()) { // 为文件时调用删除文件方法
                return deleteFile(sPath);
            } else { // 为目录时调用删除目录方法
                return deleteDirectory(sPath);
            }
        }
    }

    /**
     * 删除单个文件
     *
     * @param sPath 被删除文件的文件名
     * @return 单个文件删除成功返回true，否则返回false
     */
    public static boolean deleteFile(String sPath) {
        if (StringUtil.isEmpty(sPath)) {
            return false;
        }
        boolean flag = false;
        File file = new File(sPath);
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists()) {
            file.delete();
            flag = true;
        }
        return flag;
    }

    /**
     * 删除目录（文件夹）以及目录下的文件
     *
     * @param sPath 被删除目录的文件路径
     * @return 目录删除成功返回true，否则返回false
     */
    public static boolean deleteDirectory(String sPath) {
        if (StringUtil.isEmpty(sPath)) {
            return false;
        }
        // 如果sPath不以文件分隔符结尾，自动添加文件分隔符
        if (!sPath.endsWith(File.separator)) {
            sPath = sPath + File.separator;
        }
        File dirFile = new File(sPath);
        // 如果dir对应的文件不存在，或者不是一个目录，则退出
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return false;
        }
        boolean flag = true;
        // 删除文件夹下的所有文件(包括子目录)
        File[] files = dirFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            // 删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag)
                    break;
            } // 删除子目录
            else {
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag)
                    break;
            }
        }
        if (!flag)
            return false;
        // 删除当前目录
        if (dirFile.delete()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 检查文件是否存在
     *
     * @param filePath
     * @return
     */
    public static boolean checkFile(String filePath) {
        if (StringUtil.isEmpty(filePath)) {
            return false;
        }
        File file = new File(filePath);
        if (!file.isFile()) {
            return false;
        }
        return true;
    }

    /**
     * | FileUtils工具
     */
    public static void downloadFileFromUrl(String url, String localFilePath) throws FileException {
        LOGGER.info("【文件API】下载文件[本地文件].<START>.[url]=" + url + ",[localFilePath]=" + localFilePath);
        creatFiles(localFilePath);
        try {
            if (StringTools.contains(url, AliyunConstant.OSS_CDN_URLS)) {
                OssUtils.downloadFile(url, localFilePath);
                // } else if (url.contains("/group")) {
                // Map<String, String> paramMap = FdfsUtils.splitFilePath(url);
                // FdfsUtils.download(paramMap.get(FdfsEnum.GROUP_NAME.value),
                // paramMap.get(FdfsEnum.FILE_ID.value), localFilePath);
            } else {
                File f = new File(localFilePath);
                URL httpurl = new URL(url);
                org.apache.commons.io.FileUtils.copyURLToFile(httpurl, f);
            }
        } catch (Exception e) {
            LOGGER.error("【文件API】下载文件.[copyURLToFile]:" + e.getMessage(), e);
            throw new FileException(FileException.FILE_DOWNLOAD_FAILURE, "下载文件失败");
        }
    }

    /**
     * IOUtils工具
     */
    public static byte[] downloadByteFromUrl(String url) throws FileException {
        LOGGER.info("【文件API】下载文件[byte].<START>.[url]=" + url);
        if (StringUtil.isEmpty(url)) {
            return null;
        }
        byte[] data = null;
        try {
            if (StringTools.contains(url, AliyunConstant.OSS_CDN_URLS)) {
                data = OssUtils.downloadFile2Byte(url);
                // } else if (url.contains("/group")) {
                // Map<String, String> paramMap = FdfsUtils.splitFilePath(url);
                // data =
                // FdfsUtils.download(paramMap.get(FdfsEnum.GROUP_NAME.value),
                // paramMap.get(FdfsEnum.FILE_ID.value));
            } else {
                URL httpurl = new URL(url);
                data = IOUtils.toByteArray(httpurl);
            }
        } catch (Exception e) {
            LOGGER.error("【文件API】下载文件.[toByteArray]:" + e.getMessage(), e);
            throw new FileException(FileException.FILE_DOWNLOAD_FAILURE, "下载文件失败");
        }
        return data;
    }

    /**
     * InputStream
     */
    @Deprecated
    public static InputStream downloadStreamFromUrl(String url) throws FileException {
        try {
            URL httpurl = new URL(url);
            return httpurl.openStream();
        } catch (Exception e) {
            LOGGER.error("【文件API】下载文件.[openStream]:" + e.getMessage(), e);
            throw new FileException(FileException.FILE_DOWNLOAD_FAILURE, "下载文件失败");
        }
    }

    /**
     * 普通情况下保存字节数据到本地文件
     */
    public synchronized static boolean saveBytesTofile(File file, byte[] data) throws BizException {
        FileOutputStream fos = null;
        // 建立输出字节流
        try {
            fos = new FileOutputStream(file);
            fos.write(data); // 用FileOutputStream 的write方法写入字节数组
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 复制一个文件到另一个目录下
     *
     * @throws BizException
     */
    public static void copyFileToFolder(File inputFile, File outputFile) throws BizException {
        FileInputStream fis = null;
        FileOutputStream fos = null;

        try {
            fis = new FileInputStream(inputFile);
            fos = new FileOutputStream(outputFile);
            int len = 0;
            byte[] buf = new byte[1024];
            while ((len = fis.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }
            fis.close();
            fos.close();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    /**
     * 合并两个文件
     *
     * @param outFile  目标文件
     * @param leafFile 源文件
     */
    public static void mergeFiles(File outFile, File leafFile) {
        FileOutputStream fos = null;
        FileInputStream fis = null;
        if (!outFile.exists()) {
            try {
                outFile.createNewFile();
            } catch (Exception e) {
                throw new FileException(FileException.FILE_NOT_EXIST, "创建临时存储文件失败");
            }
        }
        try {
            // 合并其实就是文件的续写,写成true
            fos = new FileOutputStream(outFile, true);
            fis = new FileInputStream(leafFile);
            int len = 0;
            for (byte[] buf = new byte[1024 * 1024]; (len = fis.read(buf)) != -1; ) {
                fos.write(buf, 0, len);
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            try {
                if (fis != null)
                    fis.close();
                if (fos != null)
                    fos.close();
            } catch (Exception e) {
            }
        }
    }

    /**
     * 合并多个文件
     *
     * @param outFile   输出文件,
     * @param leafFiles 文件碎片集
     */
    public static void mergeFileList(File outFile, List<File> leafFiles) {
        FileOutputStream fos = null;
        FileInputStream fis = null;
        if (!outFile.exists()) {
            try {
                outFile.createNewFile();
            } catch (Exception e) {
                throw new FileException(FileException.FILE_NOT_EXIST, "创建临时存储文件失败");
            }
        }
        try {
            // 合并其实就是文件的续写,写成true
            fos = new FileOutputStream(outFile, true);
            byte[] buf = new byte[1024 * 1024];
            for (File leafFile : leafFiles) {
                fis = new FileInputStream(leafFile);
                int len = 0;
                while ((len = fis.read(buf)) != -1) {
                    fos.write(buf, 0, len);
                }
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            try {
                if (fis != null)
                    fis.close();
                if (fos != null)
                    fos.close();
            } catch (Exception e) {
            }
        }
    }

    /**
     * 音频文件转换
     *
     * @param data    音频byte数组
     * @param sourExt 原始后缀
     * @param ext     新后缀
     * @return
     * @author PENG
     */
    public static String audioFormatConvert(byte[] data, String sourExt, String ext) {
        File tmpdir = new File("tempdir/local");
        if (!tmpdir.exists()) {
            try {
                tmpdir.mkdirs();
            } catch (SecurityException ex) {
                System.out.println("无法创建临时文件夹");
                ex.printStackTrace();
            }
        }

        String name = UUID.randomUUID().toString();
        File source = new File(tmpdir, name + "." + sourExt);
        boolean creat = saveBytesTofile(source, data);
        if (creat) {
            File target = new File(tmpdir, name + "." + ext);
            AudioAttributes audio = new AudioAttributes();
            Encoder encoder = new Encoder();
            audio.setCodec("libmp3lame");

            EncodingAttributes attrs = new EncodingAttributes();
            attrs.setFormat(ext.toLowerCase());
            attrs.setAudioAttributes(audio);

            try {
                encoder.encode(source, target, attrs);
                return target.getPath();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
                return null;
            } catch (InputFormatException e) {
                e.printStackTrace();
                return null;
            } catch (EncoderException e) {
                e.printStackTrace();
                return target.getPath();
            }
        } else {
            return null;
        }
    }

    /**
     * 获取文本的内容
     *
     * @param url
     * @return
     */
    public static String getTextString(String url) {
        LOGGER.info("【文件API】获取文本的内容.<START>.[url]=" + url);
        if (StringUtil.isEmpty(url)) {
            return null;
        }
        StringBuilder lrcString = new StringBuilder();
        InputStream is = null;
        InputStreamReader isr = null;
        try {
            byte[] bt = downloadByteFromUrl(url);
            is = new ByteArrayInputStream(bt);
            boolean isUTF8Bom = CpdetectorEncoding.isUTF8Bom(is);
            if (isUTF8Bom) {
                isr = new InputStreamReader(is, "UTF-8");
            } else {
                Object charSet = CpdetectorEncoding.getEncoding(bt, false);
                if (charSet.toString().equals("UTF-8")) {
                    isr = new InputStreamReader(is, "UTF-8");
                } else {
                    isr = new InputStreamReader(new ByteArrayInputStream(bt), "GBK");
                }
            }
            BufferedReader br = new BufferedReader(isr);// 构造一个BufferedReader类来读取文件
            String s;
            while ((s = br.readLine()) != null) {// 使用readLine方法，一次读一行
                lrcString.append(System.lineSeparator() + s);
            }
        } catch (Exception e) {
            LOGGER.error("【文件工具】读取文件内容.<ERROR>:" + e.getMessage(), e);
        } finally {
            try {
                if (isr != null)
                    isr.close();
                if (is != null)
                    is.close();
            } catch (Exception e2) {
            }
        }
        LOGGER.info("【文件API】获取文本的内容.<END>");
        return lrcString.toString();
    }

    /**
     * java 8 读取文本内容
     *
     * @param url
     * @return
     */
    public static String getTextString8(String url) {
        LOGGER.info("【文件API】获取文本的内容8.<START>.[url]=" + url);
        StringBuilder result = new StringBuilder();
        String fileType = getFileType(url);
        String localFilePath = url;
        if (url.startsWith("http")) {
            localFilePath = FilePathConst.TEXT_PATH + UUIDUitl.taskName() + "." + fileType;
            downloadFileFromUrl(url, localFilePath);
        }
        try {
            Stream<String> lines = Files.lines(Paths.get(localFilePath), StandardCharsets.UTF_8);
            lines.forEach(line -> {
                result.append(line + System.lineSeparator());
            });
        } catch (Exception e) {
            LOGGER.error("【文件工具】读取文件内容8.<ERROR>:" + e.getMessage(), e);
        } finally {
            deleteFile(localFilePath);
        }
        LOGGER.info("【文件API】获取文本的内容8.<END>");
        return result.toString();
    }

    /**
     * 文件剪切,网络路径、按文件大小的百分比
     *
     * @param url
     * @param percent 1~100的整数
     * @return 返回剪切后上传后的文件路径
     * @throws BizException
     */
    public static String clipByPercent(String url, int percent) throws BizException {
        LOGGER.info("【文件API】文件剪切,按百分比.<START>.[url]=" + url);
        if (StringUtil.isEmpty(url)) {
            return null;
        }
        String fileType = FileUtils.getFileType(url);
        if (!FileUtils.VIDEO.equals(FileUtils.getGatherName(fileType))) {
            return null;
        }
        // 下载文件
        String downloadPath = FilePathConst.DOWNLOAD_PATH + UUIDUitl.generateString(32) + "." + fileType;
        FileUtils.downloadFileFromUrl(url, downloadPath);
        // 剪切文件
        String cutPath = clipByPercent(new File(downloadPath), percent);
        // 重新上传文件
        UploadResultInfo uploadResultInfo = OssUtils.uploadLocalFile(cutPath, null);
        // 删除本地文件
        FileUtils.deleteFile(downloadPath);
        FileUtils.deleteFile(cutPath);
        LOGGER.info("【文件API】文件剪切,按百分比.<END>.[uploadResultInfo]=" + uploadResultInfo);
        return uploadResultInfo == null ? null : uploadResultInfo.getUrl();
    }

    /**
     * 文件剪切,本地路径、按百分比
     *
     * @param inputFile
     * @param cutPercent 1~100的整数
     * @return 返回本地路径地址
     * @throws BizException
     */
    public static String clipByPercent(File inputFile, int cutPercent) throws BizException {
        LOGGER.info("【文件API】文件剪切.<START>.[cutPercent]=" + cutPercent);
        if (inputFile == null || inputFile.length() <= 0) {
            throw new FileException(FileException.FILE_NOT_EXIST, "不是有效的文件");
        }
        if (cutPercent > 100 || cutPercent < 1) {
            throw new FileException(FileException.FILE_READ_FAILURE, "百分比必须是1~100之间的整数");
        }
        return clipByLength(inputFile, Math.round(inputFile.length() * cutPercent / 100.0d));
    }

    /**
     * 文件剪切
     *
     * @param inputFile 待剪切的文件
     * @param cutLength 剪切长度(Byte)
     */
    public static String clipByLength(File inputFile, long cutLength) throws BizException {
        LOGGER.info("【文件API】文件剪切.<START>.[cutLength]=" + cutLength);
        if (inputFile == null || inputFile.length() <= 0) {
            throw new FileException(FileException.FILE_NOT_EXIST, "不是有效的文件");
        }
        if (cutLength < 1024) {
            throw new FileException(FileException.FILE_READ_FAILURE, "剪切的文件必须大于1KB");
        }
        // 输出文件路径
        String outFilePath = FilePathConst.CUT_PATH + UUIDUitl.generateString(12) + "_" + inputFile.getName();
        creatFiles(outFilePath);
        // 剪切长度
        cutLength = cutLength > inputFile.length() ? inputFile.length() : cutLength;
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            // 两个输入流
            bis = new BufferedInputStream(new FileInputStream(inputFile));
            // 缓冲字节输出流
            bos = new BufferedOutputStream(new FileOutputStream(outFilePath));
            // 读取多少次
            long index = cutLength % 1024 > 0 ? cutLength / 1024 + 1 : cutLength / 1024;
            int mod = (int) cutLength % 1024;
            byte[] bt = new byte[1024];
            int len = 0;
            for (int i = 0; i < index; i++) {
                if (i + 1 == index && mod != 0) {
                    bt = new byte[mod];
                    len = bis.read(bt);
                } else {
                    len = bis.read(bt);
                }
                if (len != -1) {
                    bos.write(bt, 0, len);
                }
            }
            bos.flush();
        } catch (IOException e) {
            LOGGER.error("【文件工具】剪切文件内容:" + e.getMessage(), e);
            throw new FileException(FileException.FILE_READ_FAILURE, "文件剪切失败");
        } finally {
            try {
                if (bis != null)
                    bis.close();
                if (bos != null)
                    bos.close();
            } catch (IOException e) {
                LOGGER.error("【文件工具】文件流关闭失败:" + e.getMessage(), e);
            }
        }
        LOGGER.info("【文件API】文件剪切.<END>");
        return outFilePath;
    }

    /**
     * 将文件转成base64 字符串
     *
     * @param data
     * @return *
     * @throws Exception
     */
    public static String encodeBase64File(byte[] data) throws BizException {
        try {
            return new String(Base64.encodeBase64(data));
        } catch (Exception e) {
            LOGGER.error("【文件工具】将base64字符解码保存文件失败:" + e.getMessage(), e);
            return null;
        }
    }

    /**
     * 将base64字符解码保存文件
     *
     * @param base64Code
     * @param targetPath
     * @throws Exception
     */
    public static boolean decoderBase64File(String base64Code, String targetPath) throws BizException {
        if (base64Code == null)
            return false;
        OutputStream out = null;
        try {
            // Base64解码
            byte[] b = Base64.decodeBase64(targetPath);
            for (int i = 0; i < b.length; ++i) {
                if (b[i] < 0) {// 调整异常数据
                    b[i] += 256;
                }
            }
            // 生成jpeg图片
            out = new FileOutputStream(targetPath);
            out.write(b);
            out.flush();
        } catch (Exception e) {
            LOGGER.error("【文件工具】将base64字符解码保存文件失败:" + e.getMessage(), e);
            return false;
        } finally {
            try {
                if (out != null)
                    out.close();
            } catch (Exception e) {
            }
        }
        return true;
    }

    /**
     * 本地文件到byte数组
     *
     * @param path
     * @return
     */
    public static byte[] file2byte(String path) {
        byte[] data = null;
        FileInputStream fis = null;
        ByteArrayOutputStream os = null;
        try {
            fis = new FileInputStream(new File(path));
            os = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int numBytesRead = 0;
            while ((numBytesRead = fis.read(buf)) != -1) {
                os.write(buf, 0, numBytesRead);
            }
            data = os.toByteArray();
        } catch (Exception e) {
            LOGGER.error("【文件工具】文件到byte数组:" + e.getMessage(), e);
        } finally {
            try {
                if (fis != null)
                    fis.close();
                if (os != null)
                    os.close();
            } catch (IOException e) {
                LOGGER.error("【文件工具】文件流关闭失败:" + e.getMessage(), e);
            }
        }
        return data;
    }

    /**
     * 从输入流中获取数据
     *
     * @param inStream 输入流
     * @return
     * @throws Exception
     */
    public static byte[] readInputStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        try {
            while ((len = inStream.read(buffer)) != -1) {
                outStream.write(buffer, 0, len);
            }
        } catch (Exception e) {
            LOGGER.error("【文件工具】文件流到byte数组:" + e.getMessage(), e);
        } finally {
            inStream.close();
        }
        return outStream.toByteArray();
    }

}
