com.feilong.io.FilenameUtil Maven / Gradle / Ivy
Show all versions of feilong Show documentation
/*
* Copyright (C) 2008 feilong
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.feilong.io;
import static com.feilong.core.Validator.isNotNullOrEmpty;
import static com.feilong.core.Validator.isNullOrEmpty;
import static com.feilong.core.util.CollectionsUtil.newArrayList;
import java.io.File;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.feilong.core.Validate;
import com.feilong.core.lang.StringUtil;
import com.feilong.lib.io.FilenameUtils;
import com.feilong.lib.lang3.StringUtils;
/**
* The Class FilenameUtil.
*
* @author feilong
* @see com.feilong.lib.io.FilenameUtils
* @since 1.4.0
*/
public final class FilenameUtil{
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory.getLogger(FilenameUtil.class);
/**
* 文件名称由文件名和扩展名组成,两者由小黑点分隔,扩展名通常是用来表示文件的类 别.
*
* Windows 中整个文件名称最长 255 个字符(一个中文字算两个字符);
* DOS 中,文件名最长 8 字符,扩展名最长 3 字符,故又称 DOS 8.3 命名规则.
* 文件名称可仅有前半部,即无扩展名,如文件名称最短可以是 " 1 " 、 " C " 等.
* 给文件命名还应注意以下规则:
*
*
* - 文件名不能包含下列任何字符之一(共 9 个): \/:*?"<>|
* - 不能单独使用 " 设备名 " 作文件名. " 设备名 " 包括: con , aux , com0 ~ com9 , lpt0 ~ lpt9 , nul , prn
* - 文件名不区分大小写,如 A.txt 和 a.TxT 表示同一文件
*
*
* @see 错误消息: 文件名是无效的或不能包含任何以下字符
* @since 1.0.7
*/
private static final String[][] MICROSOFT_PC = { //
// { "\\", "" }, // \
// { "/", "" }, // /
{ "\"", "" }, // "
{ ":", "" }, // :
{ "*", "" }, // *
{ "?", "" }, // ?
{ "<", "" }, // <
{ ">", "" }, // >
{ "|", "" }, // |
};
/** Don't let anyone instantiate this class. */
private FilenameUtil(){
//AssertionError不是必须的. 但它可以避免不小心在类的内部调用构造器. 保证该类在任何情况下都不会被实例化.
//see 《Effective Java》 2nd
throw new AssertionError("No " + getClass().getName() + " instances for you!");
}
//---------------------------------------------------------------
/**
* 不同的操作系统 对系统文件名称有要求,此方法的作用就是处理这些文件名称.
*
* @param fileName
* 文件名称
* @return 可用的文件名称
* @see #MICROSOFT_PC
* @since 1.0.7
*/
public static String getFormatFileName(final String fileName){
String formatFileName = fileName;
for (int i = 0, j = MICROSOFT_PC.length; i < j; ++i){
String[] arrayElement = MICROSOFT_PC[i];
String oldChar = arrayElement[0];
if (formatFileName.contains(oldChar)){
String newChar = arrayElement[1];
LOGGER.warn("formatFileName:[{}] contains oldChar:[{}],will replace newChar:[{}]", formatFileName, oldChar, newChar);
formatFileName = StringUtil.replace(formatFileName, oldChar, newChar);
}
}
return formatFileName;
}
//---------------------------------------------------------------
/**
* 获得带后缀的文件纯名称.
*
*
* 如:F:/pie2.png,返回 pie2.png
*
*
* @param fileName
* the file name
* @return 如果 fileName
是null,抛出 {@link NullPointerException}
* 如果 fileName
是blank,抛出 {@link IllegalArgumentException}
* @see java.io.File#getName()
* @see com.feilong.lib.io.FilenameUtils#getName(String)
*/
public static String getFileName(String fileName){
Validate.notBlank(fileName, "fileName can't be blank!");
File file = new File(fileName);
return file.getName();
}
/**
* 获得文件的不带后缀名的名称.
*
*
* Example 1:
* F:/pie2.png, 返回 F:/pie2
*
* Example 2:
* pie2.png, 返回 pie2
*
*
* @param fileName
* 文件名称
* @return 获得文件的不带后缀名的名称
* @see java.lang.String#substring(int, int)
* @see "org.apache.commons.io.FilenameUtils#getBaseName(String)"
*/
public static String getFilePreName(String fileName){
return fileName.substring(0, fileName.lastIndexOf('.'));
}
//---------------------------------------------------------------
/**
* 获得文件后缀名(不带. 的后缀),并返回原样字母.
*
*
* 如果文件没有后缀名 返回 "" (EMPTY)
*
*
* 示例:
*
*
*
*
* F:/pie2.png 返回 png
* F:/pie2 返回 {@link StringUtils#EMPTY}
*
*
*
*
* Gets the extension of a filename.
*
* This method returns the textual part of the filename after the last dot. There must be no directory separator after the dot.
*
*
* foo.txt {@code -->} "txt"
* a/b/c.jpg {@code -->} "jpg"
* a/b.txt/c {@code -->} ""
* a/b/c {@code -->} ""
*
*
* The output will be the same irrespective of the machine that the code is running on.
*
* @param fileName
* 文件名称
* @return 不带. 的后缀,
* 如果 fileName
是null,返回 {@link StringUtils#EMPTY}
* @see com.feilong.lib.io.FilenameUtils#getExtension(String)
* @see java.lang.String#substring(int, int)
* @since 1.4.0
*/
public static String getExtension(String fileName){
return StringUtils.defaultString(FilenameUtils.getExtension(fileName));
}
/**
* 获得文件后缀名,并返回小写字母.
*
*
示例:
*
*
*
*
* FilenameUtil.getExtensionLowerCase("苍老师.AVI") = "avi"
* FilenameUtil.getExtensionLowerCase("苍老师") = ""
* FilenameUtil.getExtensionLowerCase(".苍老师") = "苍老师"
*
*
*
*
* 说明:
*
*
* - 如果文件没有后缀名, 返回 {@link com.feilong.lib.lang3.StringUtils#EMPTY}
*
*
*
*
* @param fileName
* 文件名称
* @return 如果 fileName
是null,抛出 {@link NullPointerException}
* 如果 fileName
是blank,抛出 {@link IllegalArgumentException}
* @see com.feilong.lib.io.FilenameUtils#getExtension(String)
* @see #getExtension(String)
* @since 1.7.1
*/
public static String getExtensionLowerCase(String fileName){
Validate.notBlank(fileName, "fileName can't be blank!");
return getExtension(fileName).toLowerCase();
}
// [start] 解析文件名称
/**
* 将一个文件使用新的文件后缀,其余部分不变.
*
*
* 如果一个文件没有后缀,将会添加 .+newPostfixName
*
*
* Example 1:
*
*
* String fileName="F:/pie2.png";
* FileUtil.getNewFileName(fileName, "gif")
*
* return F:/pie2.gif
*
*
* @param fileName
* 文件名称,比如 F:/pie2.png
* @param newPostfixName
* 不带.号, 比如 gif
* @return 如果 fileName
是null,抛出 {@link NullPointerException}
* 如果 fileName
是blank,抛出 {@link IllegalArgumentException}
* 如果 newPostfixName
是null,抛出 {@link NullPointerException}
* 如果 newPostfixName
是blank,抛出 {@link IllegalArgumentException}
*/
public static String getNewFileName(String fileName,String newPostfixName){
Validate.notBlank(fileName, "fileName can't be null/empty!");
Validate.notBlank(newPostfixName, "newPostfixName can't be null/empty!");
//---------------------------------------------------------------
// 有后缀
if (hasExtension(fileName)){
return fileName.substring(0, fileName.lastIndexOf('.') + 1) + newPostfixName;
}
//---------------------------------------------------------------
// 没有后缀直接拼接
return fileName + "." + newPostfixName;
}
// [end]
//---------------------------------------------------------------
/**
* 获得文件的最顶层 父文件夹名称.
*
*
* Example 1:
* "mp2-product\\mp2-product-impl\\src\\main\\java\\com\\mp2\\rpc\\impl\\item\\repo\\package-info.java"
*
* 返回 mp2-product
*
*
* @param pathname
* 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例.
* @return 如果没有父文件夹,返回自己,比如 E:/ 直接返回 E:/
* 如果 pathname
是blank,抛出 {@link IllegalArgumentException}
* @since 1.0.7
*/
public static String getFileTopParentName(String pathname){
Validate.notBlank(pathname, "pathname can't be null/empty!");
String parent = FileUtil.getParent(pathname);
if (isNullOrEmpty(parent)){
return pathname;
}
//---------------------------------------------------------------
//递归
String fileTopParentName = getFileTopParentName(parent);
LOGGER.debug("pathname:[{}],fileTopParentName:[{}]", pathname, fileTopParentName);
return fileTopParentName;
}
/**
* 获得文件的最顶层父文件夹名称.
*
*
* Example 1:
* "mp2-product\\mp2-product-impl\\src\\main\\java\\com\\mp2\\rpc\\impl\\item\\repo\\package-info.java"
*
* 返回 mp2-product
*
*
* @param file
* the file
* @return 如果没有父文件夹,返回自己,比如 E:/ 直接返回 E:/
* @since 1.0.7
*/
public static String getFileTopParentName(File file){
Validate.notNull(file, "file can't be null!");
File parent = file.getParentFile();
if (isNullOrEmpty(parent)){
String patch = file.getPath();//E:/--->E:\
LOGGER.debug("parent isNullOrEmpty,return file patch:{}", patch);
return patch;
}
//---------------------------------------------------------------
//递归
String fileTopParentName = getFileTopParentName(parent);
LOGGER.debug("file.getAbsolutePath():[{}],fileTopParentName:[{}]", file.getAbsolutePath(), fileTopParentName);
return fileTopParentName;
}
//---------------------------------------------------------------
/**
* 获得路径的所有 parent路径.
*
*
* 目前的使用场景有, 创建远程sftp目录地址
*
*
* 示例:
*
*
*
*
* String remoteDirectory = "/home/sftp-speedo/test/aa/bbb/ccc/ddd/201606160101/";
* List{@code } list = getParentPathList(remoteDirectory);
* LOGGER.debug(JsonUtil.format(list));
*
*
*
* 返回:
*
*
* [
* "/home/sftp-speedo/test/aa/bbb/ccc/ddd",
* "/home/sftp-speedo/test/aa/bbb/ccc",
* "/home/sftp-speedo/test/aa/bbb",
* "/home/sftp-speedo/test/aa",
* "/home/sftp-speedo/test",
* "/home/sftp-speedo",
* "/home",
* "/"
* ]
*
*
*
*
*
* @param path
* the path
* @return 如果 path
是null,抛出 {@link NullPointerException}
* 如果 path
是blank,抛出 {@link IllegalArgumentException}
* @since 1.7.1
*/
public static List getParentPathList(String path){
Validate.notBlank(path, "path can't be blank!");
List list = newArrayList();
resolverGetParentPath(path, list);
return list;
}
//---------------------------------------------------------------
/**
* Resolver get parent path.
*
* @param path
* the path
* @param list
* the list
*/
private static void resolverGetParentPath(String path,List list){
String parent = FileUtil.getParent(path);
if (null != parent){
parent = StringUtil.replace(parent, "\\", "/");
if (isNotNullOrEmpty(parent)){
list.add(parent);
resolverGetParentPath(parent, list);//级联
}
}
}
/**
* 判断文件名是否有后缀.
*
* @param fileName
* the file name
* @return true, if successful
* @see com.feilong.lib.io.FilenameUtils#indexOfExtension(String)
* @since 1.4.0
* @since 1.11.0 change to private
*/
private static boolean hasExtension(String fileName){
return StringUtils.INDEX_NOT_FOUND != FilenameUtils.indexOfExtension(fileName);
}
}