All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yixan.tools.common.util.FileUtil Maven / Gradle / Ivy

There is a newer version: 3.7.1
Show newest version
package com.yixan.tools.common.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.LinkedList;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 文件工具类
 *
 * @author zhaohuihua
 */
public class FileUtil {

    /** 日志对象 **/
    private static Logger log = LoggerFactory.getLogger(FileUtil.class);

    /** 斜杠/ **/
    public static final String SLASH = "/";

    /** 反斜杠\ **/
    public static final String BSLASH = "\\";

    /**
     * 获取文件扩展名
* /image/abc.def.png --> .png
* /image/abcdef/png --> null
* /image/abc.def/png --> null
* * @param fileName * @return */ public static String getExtension(String fileName) { return getExtension(fileName, true); } /** * 获取文件扩展名 * * @param path * @param dot 带不带点, 如true=.png, false=png * @return */ public static String getExtension(String path, boolean dot) { if (path == null) { return null; } int i = path.lastIndexOf('.'); if (i < 0 || i < path.lastIndexOf('/') || i < path.lastIndexOf('/')) { return null; } return path.substring(i + (dot ? 0 : 1)); } /** * 清除文件扩展名
* /image/abc.def.png --> /image/abc.def
* /image/abcdef/png --> /image/abcdef/png
* /image/abc.def/png --> /image/abc.def/png
* * @param path * @return */ public static String removeExtension(String path) { if (path == null) { return null; } int i = path.lastIndexOf('.'); if (i < 0 || i < path.lastIndexOf('/') || i < path.lastIndexOf('/')) { return null; } return path.substring(0, i); } /** * 替换文件扩展名
* /image/abc.def.png, .jpg --> /image/abc.def.jpg
* /image/abcdef/xxx, .jpg --> /image/abcdef/xxx.jpg
* /image/abc.def/xxx, .jpg --> /image/abc.def/xxx.jpg
* * @param path * @return */ public static String replaceExtension(String path, String extension) { String newpath = removeExtension(path); if (newpath == null || extension == null) { return newpath; } else if (extension.startsWith(".")) { return newpath + extension; } else { return newpath + "." + extension; } } /** * 获取类路径 * * @author 赵卉华 * @return 绝对路径 */ public static String getClassPath() { return getClassPathResource(null); } /** * 获取类路径下的资源 * * @author 赵卉华 * @param path 相对路径 * @return 绝对路径 */ public static String getClassPathResource(String path) { URL root = FileUtil.class.getResource(path == null ? "/" : concat("/", path)); if (root == null) { // String classPath = getAbsolutePathOfLocalFileUrl(FileUtil.class.getResource("/")); // log.error("File not found: {}, ClassPath: {}", path, classPath); return null; } else { return getAbsolutePathOfLocalFileUrl(root); } } /** * 获取类文件夹路径 * * @author 赵卉华 * @param clazz 类文件 * @return 绝对路径 */ public static String getClassFolder(Class clazz) { return getClassFolderResource(clazz, null); } /** * 获取与指定类文件夹路径下的资源 * * @author 赵卉华 * @param clazz 类文件 * @param path 相对路径 * @return 绝对路径 */ public static String getClassFolderResource(Class clazz, String path) { URL root = clazz.getResource(path == null ? "./" : concat("./", path)); if (root == null) { // String classPath = getAbsolutePathOfLocalFileUrl(FileUtil.class.getResource("/")); // log.error("File not found: {}, ClassPath: {}", path, classPath); return null; } else { return getAbsolutePathOfLocalFileUrl(root); } } private static String getAbsolutePathOfLocalFileUrl(URL url) { try { // root.getPath(), 文件路径中有空格的话会出问题, 被替换为%20 // root.toURI(), 支持文件路径中有空格的情况 return new File(url.toURI()).getAbsolutePath(); } catch (URISyntaxException e) { return new File(url.getPath()).getAbsolutePath(); } } /** * 通过网络下载文件 * * @param url URL * @return 文件内容 * @throws IOException 失败 */ public static byte[] download(String url) throws IOException { try (InputStream input = new URL(url).openStream(); ByteArrayOutputStream output = new ByteArrayOutputStream();) { // Files.copy(Paths.get(URI.create(url)), output); // 不支持HTTP协议 copy(input, output); return output.toByteArray(); } } /** * 通过网络下载文件 * * @param url URL * @return 文件内容 * @throws IOException 失败 */ public static String downloadString(String url) throws IOException { return downloadString(url, "UTF-8"); } /** * 通过网络下载文件 * * @param url URL * @param charset 字符编码格式 * @return 文件内容 * @throws IOException 失败 */ public static String downloadString(String url, String charset) throws IOException { return new String(download(url), charset); } /** * 通过网络下载文件 * * @param url URL * @param saveAs 保存的文件路径 * @throws IOException 失败 */ public static void downloadSave(String url, String saveAs) throws IOException { try (InputStream input = new URL(url).openStream(); OutputStream output = new FileOutputStream(new File(saveAs))) { // Files.copy(Paths.get(URI.create(url)), output); // 不支持HTTP协议 copy(input, output); } } public static void copy(InputStream input, OutputStream output) throws IOException { int length; int bz = 2048; byte[] buffer = new byte[bz]; while ((length = input.read(buffer, 0, buffer.length)) > 0) { output.write(buffer, 0, length); } } /** * 将byte数据保存到文件 * * @param data 数据 * @param path 文件路径 * @throws IOException 失败 */ public static void saveFile(byte[] data, String path) throws IOException { Path target = Paths.get(path); mkdirsIfNotExists(target); try (InputStream in = new ByteArrayInputStream(data)) { Files.copy(in, target); } } /** * 将InputStream数据保存到文件 * * @param data 数据 * @param path 文件路径 * @throws IOException 失败 */ public static void saveFile(InputStream input, String path) throws IOException { Path target = Paths.get(path); mkdirsIfNotExists(target); Files.copy(input, target); } /** * 如果文件的文件夹不存在则创建 * * @param file 必须是文件, 不能是文件夹 */ public static void mkdirsIfNotExists(File file) { mkdirsIfNotExists(file.toPath()); } /** * 如果文件夹不存在则创建 * * @param file * @param toParent 是判断file自身还是判断file的父级文件夹 */ public static void mkdirsIfNotExists(File file, boolean toParent) { mkdirsIfNotExists(file.toPath(), toParent); } private static void mkdirsIfNotExists(Path path) { mkdirsIfNotExists(path, true); } private static void mkdirsIfNotExists(Path path, boolean toParent) { Path folder = toParent ? path.getParent() : path; if (!Files.exists(folder)) { folder.toFile().mkdirs(); } } /** * 移动文件或文件夹 * * @param source * @param destination * @throws IOException */ public static void move(String source, String destination) throws IOException { move(Paths.get(source), Paths.get(destination)); } /** * 移动文件或文件夹 * * @param source * @param destination * @throws IOException */ public static void move(Path source, Path destination) throws IOException { // normalize()清除路径中的.和.. Path spath = source.normalize(); Path dpath = destination.normalize(); if (!Files.exists(spath)) { // 源文件不存在 return; } if (spath.toString().equals(dpath.toString())) { return; // 源路径和目标路径相同 } if (Files.isDirectory(spath) && (Files.exists(dpath) || dpath.startsWith(spath))) { // 是文件夹, 目标路径已经存在或目标路径在源路径之中, 则只能遍历一个个的移动 File sfile = spath.toFile(); File[] files = sfile.listFiles(); for (File next : files) { move(next.toPath(), dpath.resolve(next.getName())); } if (sfile.exists() && sfile.listFiles().length == 0) { // 删除空文件夹 sfile.delete(); } } else { // 目标路径的上级文件夹必须存在, 否则会报错 mkdirsIfNotExists(dpath, true); // 开始移动 try { Files.move(spath, dpath); } catch (IOException e) { log.error("Move file failed: {} --> {}\n\t{}", spath, dpath, e.toString()); throw e; } } } /** * 复制文件或文件夹 * * @param source * @param destination * @throws IOException */ public static void copy(String source, String destination) throws IOException { copy(Paths.get(source), Paths.get(destination)); } /** * 复制文件或文件夹 * * @param source * @param destination * @throws IOException */ public static void copy(Path source, Path destination) throws IOException { if (!Files.exists(source)) { // 源文件不存在 throw new FileNotFoundException(source.toString()); } // Files.copy目标路径的上级文件夹必须存在, 否则会报错, 所以先创建上级文件夹 mkdirsIfNotExists(destination, true); if (Files.isDirectory(source)) { // 复制文件夹 Files.walkFileTree(source, new CopyFileVisitor(source, destination)); } else { // 复制文件 Files.copy(source, destination); } } /** * 删除文件或文件夹 * * @param source * @throws IOException */ public static void delete(String source) throws IOException { delete(Paths.get(source)); } /** * 删除文件或文件夹 * * @param source * @throws IOException */ public static void delete(Path source) throws IOException { if (!Files.exists(source)) { return; // 源文件不存在 } if (Files.isDirectory(source)) { // 删除文件夹 Files.walkFileTree(source, new DeleteFileVisitor()); } else { // 删除文件 try { Files.deleteIfExists(source); } catch (IOException e) { log.error("Delete file failed: {}\n\t{}", source, e.toString()); } } } /** * 文件Visitor * * @author zhaohuihua * @version V1.0 2016年12月24日 */ private static abstract class AllFileVisitor implements FileVisitor { @Override public FileVisitResult preVisitDirectory(Path directory, BasicFileAttributes attrs) throws IOException { onPreVisitDirectory(directory); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path directory, IOException e) throws IOException { onPostVisitDirectory(directory); if (e != null) { log.error("Visit directory failed: {}\n\t{}", directory.toString(), e.toString()); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { onVisitFile(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException e) { log.error("Visit file failed: {}\n\t{}", file.toString(), e.toString()); return FileVisitResult.CONTINUE; } protected void onVisitFile(Path file) { } protected void onPreVisitDirectory(Path directory) { } protected void onPostVisitDirectory(Path directory) { } } private static class CopyFileVisitor extends AllFileVisitor { private Path source; private Path destination; public CopyFileVisitor(Path source, Path destination) { this.source = source; this.destination = destination; } protected void onVisitFile(Path file) { Path relative = source.relativize(file); Path absolute = destination.resolve(relative); try { mkdirsIfNotExists(absolute); Files.copy(file, absolute); } catch (IOException e) { log.error("Copy file failed: {} --> {}\n\t{}", file, absolute, e.toString()); } } } private static class DeleteFileVisitor extends AllFileVisitor { protected void onVisitFile(Path file) { try { Files.deleteIfExists(file); } catch (IOException e) { log.error("Delete file failed: {}\n\t{}", file, e.toString()); } } protected void onPostVisitDirectory(Path directory) { try { Files.deleteIfExists(directory); } catch (IOException e) { log.error("Delete directory failed: {}\n\t{}", directory, e.toString()); } } } /** * 连接 * * @param folder 文件夹 * @param paths 文件路径 * @return */ public static String concat(String folder, String... paths) { return concat(false, folder, paths); } /** * 连接 * * @param format 要不要格式化 * @param folder 文件夹 * @param paths 文件路径 * @return */ public static String concat(boolean format, String folder, String... paths) { StringBuilder buffer = new StringBuilder(); if (VerifyUtil.isNotBlank(folder)) { buffer.append(folder); } for (String path : paths) { if (VerifyUtil.isBlank(path)) { continue; } if (!endsWithSeparator(buffer) && !startsWithSeparator(path)) { buffer.append(SLASH).append(path); } else if (endsWithSeparator(folder) && startsWithSeparator(path)) { buffer.append(path.substring(1)); } else { buffer.append(path); } } return format ? formatPath(buffer.toString()) : buffer.toString(); } private static boolean endsWithSeparator(CharSequence string) { return string.toString().endsWith(SLASH) || string.toString().endsWith(BSLASH); } private static boolean startsWithSeparator(CharSequence string) { return string.toString().startsWith(SLASH) || string.toString().startsWith(BSLASH); } private static Pattern SCHEMA = Pattern.compile("(file|ftp|jar|https?)\\:/+"); private static String replaceProtocol(String path) { int index = 0; StringBuilder buffer = new StringBuilder(); Matcher m = SCHEMA.matcher(path); while (m.find()) { buffer.append(path.substring(index, m.start(1))); String schema = m.group(1); buffer.append(schema).append("://"); if ("file".equals(schema)) { // file:/D:/domain/index.html file:/D:/还是file://D:/还是file:///D:/ // chrome打开本地文件显示为三个斜杠; // new URL()只能识别一个或三个斜杠, 两个斜杠就报错UnknownHostException: D // 统一处理成三个斜杠 buffer.append("/"); } index = m.end(); } buffer.append(path.substring(index)); return buffer.toString(); } /** * 格式化路径, \替换为/, 并处理../和./的情况
* 路径作为URL时不能使用\, 但不论windows还是linux, Java环境都支持/
* 因此将所有的\替换为/
* 如 \\site\\home\\../include/head.tpl 处理为 /site/include/head.tpl * * @author 赵卉华 * @param path 原路径 * @return 格式化之后的路径 */ public static String formatPath(String path) { if (VerifyUtil.isBlank(path)) { return path; } StringTokenizer tokenizer = new StringTokenizer(path, "\\/"); LinkedList list = new LinkedList(); while (tokenizer.hasMoreTokens()) { String string = tokenizer.nextToken(); if (VerifyUtil.isBlank(string)) { continue; } else if (".".equals(string)) { continue; } else if ("..".equals(string)) { if (list.isEmpty()) { continue; } else { list.removeLast(); } } else { list.addLast(string); } } StringBuilder buffer = new StringBuilder(); for (String string : list) { if (buffer.length() > 0) { buffer.append(SLASH); } else { if (path.startsWith(SLASH) || path.startsWith(BSLASH)) { buffer.append(SLASH); } } buffer.append(string); } return replaceProtocol(buffer.toString()); } /** * 将path转换为相对于root的路径
* 不用Files.relativize()是为了兼容URL * *
     * String root = "D:/domain/biz/"
     * relativize(root, "D:/domain/biz/index.html") -- index.html
     * relativize(root, "D:/domain/biz/html/homepage.html") -- html/homepage.html
     * relativize(root, "D:/domain/assets/libs/mui/mui.js") -- ../assets/libs/mui/mui.js
     * relativize(root, "D:/static/assets/libs/mui/mui.js") -- ../../static/assets/libs/mui/mui.js
     * 
* * @param root 根路径 * @param path 绝对路径 * @return 相对路径 */ public static String relativize(String root, String path) { String[] roots = splitPath(root); String[] paths = splitPath(path); // 先找到第一个不相同的文件夹 int len = Math.min(roots.length, paths.length); int idx = len; for (int i = 0; i < len; i++) { if (!roots[i].equals(paths[i])) { idx = i; break; } } StringBuilder buffer = new StringBuilder(); // 从第一个不相同的开始, roots还有几级就加几个../ for (int i = idx; i < roots.length; i++) { buffer.append("..").append("/"); } // 从第一个不相同的开始, 加上paths剩下的路径 for (int i = idx; i < paths.length; i++) { if (i > idx) { buffer.append("/"); } buffer.append(paths[i]); } return replaceProtocol(buffer.toString()); } private static Pattern separator = Pattern.compile("/+"); private static Pattern trim = Pattern.compile("^/+|/+$"); public static String[] splitPath(String path) { path = formatPath(path); path = trim.matcher(path).replaceAll(""); return separator.split(path); } /** 判断是不是绝对路径 **/ public static boolean isAbsolutePath(String path) { // 绝对路径: linux的/home/或window的D:/xxx/或http://url return path.startsWith("/") || path.startsWith("\\") || path.indexOf(':') >= 0; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy