1. File对象
Java File是文件对象的抽象,对应于一个目录或一个文件。
当使用pathname创建一个File对象的时,该目录可以不存在,但在对该对象进行一些操作,比如创建一个FileOutputStream时,如果文件目录不存在,则可能会报错,比如:
public void test1() throws IOException {
// /Users/wuhao目录下不存在目录aa
File file = new File("/Users/wuhao/aa/a.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(1);
}
执行结果:
java.io.FileNotFoundException: /Users/wuhao/aa/a.txt (No such file or directory)
at java.io.FileOutputStream.open0(Native Method)
at java.io.FileOutputStream.open(FileOutputStream.java:270)
at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
at java.io.FileOutputStream.<init>(FileOutputStream.java:162)
at Test.test1(Test.java:23)
public void test2() throws IOException {
// /Users/wuhao目录下不存在目录aa/bb
File file = new File("/Users/wuhao/aa/bb/a.txt");
// 使用File的mkdirs()方法创建必要的父目录
file.getParentFile().mkdirs();
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(1);
}
2. File对象的操作
2.1 移动文件(复制文件)
/**
* 复制文件
* 方法一: 使用文件流
*
* @param source 源文件
* @param dest 目标文件
*/
private static void copyFileUsingFileStreams(File source, File dest)
throws IOException {
InputStream input = null;
OutputStream output = null;
try {
input = new FileInputStream(source);
output = new FileOutputStream(dest);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = input.read(buf)) > 0) {
output.write(buf, 0, bytesRead);
}
} finally {
input.close();
output.close();
}
}
/**
* 复制文件
* 方法二: 使用NIO的FileChannel复制
*
* @param source 源文件
* @param dest 目标文件
*/
private static void copyFileUsingFileChannels(File source, File dest) throws IOException {
FileChannel inputChannel = null;
FileChannel outputChannel = null;
try {
inputChannel = new FileInputStream(source).getChannel();
outputChannel = new FileOutputStream(dest).getChannel();
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
} finally {
inputChannel.close();
outputChannel.close();
}
}
/**
* 复制文件
* 方法三: 使用Apache Commons IO提供的工具类进行复制,这个类主要使用了Java NIO Channel
*
* @param source 源文件
* @param dest 目标文件
*/
private static void copyFileUsingApacheCommonsIO(File source, File dest)
throws IOException {
FileUtils.copyFile(source, dest);
}
/**
* 复制文件
* 方法四: 使用Java7的Files类复制
*
* @param source 源文件
* @param dest 目标文件
*/
private static void copyFileUsingJava7Files(File source, File dest)
throws IOException {
Files.copy(source.toPath(), dest.toPath());
}
2.2 查找文件
/**
* 寻找dir目录下文件名为filename的子文件
*
* @param dir 根目录
* @param filename 待查找的文件名
* @return
*/
public static File findFile(String dir, String filename) {
File file = new File(dir);
if (!file.exists() || !file.isDirectory()) {
return null;
}
File[] files = file.listFiles();
for (File subFile : files) {
if (subFile.isDirectory()) {
File result = findFile(subFile.getAbsolutePath(), filename);
if (result != null) {
return result;
}
} else if (subFile.getName().equals(filename)) {
return subFile;
}
}
return null;
}
2.3 删除文件
public static void delete(String path) {
if (StringUtils.isBlank(path)) {
return;
}
File f = new File(path);
if (!f.exists()) {
return;
}
if (f.isDirectory() && f.list() != null) {
//如果是目录,先递归删除
String[] list = f.list();
for (int i = 0; i < list.length; i++) {
delete(path + File.separator + list[i]);//先删除目录下的文件
}
}
// 如果File是目录,必须是空目录才能删除,因此需要递归整个目录,将其子文件依次删除
f.delete();
}
2.4 压缩、解压文件
Zip包的压缩类为ZipOutputStream和ZipInputStream,属于InputStream和OutputStream的继承层次。压缩类的使用非常直观——直接将输出流封装为ZipOutputStream,并将输入流封装成ZipInputStream即可。
/**
* 压缩成ZIP
*
* @param srcDir 压缩文件夹路径
* @param out 压缩文件输出流
* @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
* false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
* @throws MarketException 压缩失败会抛出运行时异常
*/
public static void toZip(String srcDir, OutputStream out, boolean keepDirStructure)
throws RuntimeException {
long start = System.currentTimeMillis();
ZipOutputStream zos = null;
try {
// ZipOutputStream属于OutputStream继承层次
zos = new ZipOutputStream(out);
File sourceFile = new File(srcDir);
// 核心方法
compress(sourceFile, zos, sourceFile.getName(), keepDirStructure);
long end = System.currentTimeMillis();
log.info("压缩完成,耗时:{} ms", end - start);
} catch (Exception e) {
throw new MarketException(ResultStatus.PARAM_ERR, "压缩文件时失败", e);
} finally {
if (zos != null) {
try {
zos.close();
} catch (IOException e) {
log.error("zos关闭失败", e);
}
}
}
}
/**
* 递归压缩方法
*
* @param sourceFile 源文件
* @param zos zip输出流
* @param name 压缩后的名称
* @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
* false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
* @throws Exception
*/
private static void compress(File sourceFile, ZipOutputStream zos, String name,
boolean keepDirStructure) throws Exception {
byte[] buf = new byte[BUFFER_SIZE];
if (sourceFile.isFile()) {
// 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
// 对于每个要加入压缩档案的文件,都必须调用putNextEntry(),并将其传递给一个ZipEntry对象
zos.putNextEntry(new ZipEntry(name));
// copy文件到zip输出流中
int len;
FileInputStream in = new FileInputStream(sourceFile);
while ((len = in.read(buf)) != -1) {
zos.write(buf, 0, len);
}
// Complete the entry
zos.closeEntry();
in.close();
} else {
File[] listFiles = sourceFile.listFiles();
if (listFiles == null || listFiles.length == 0) {
// 需要保留原来的文件结构时,需要对空文件夹进行处理
if (keepDirStructure) {
// 空文件夹的处理
zos.putNextEntry(new ZipEntry(name + File.separator));
// 没有文件,不需要文件的copy
zos.closeEntry();
}
} else {
for (File file : listFiles) {
// 判断是否需要保留原来的文件结构
if (keepDirStructure) {
// 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
// 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
compress(file, zos, name + File.separator + file.getName(), keepDirStructure);
} else {
// 将所有文件放到根目录下
compress(file, zos, file.getName(), keepDirStructure);
}
}
}
}
}
/**
* 解压缩zip文件
* 解压zip文件有一个简便的方法——利用ZipFile对象读取文件,该对象有一个entries()方法,用来返回一个ZipEntry对象的Enumeration
* (枚举)
*
* @param srcFile
* @param destDirPath
* @return 返回解压后目录的地址
* @throws RuntimeException
*/
public static String unZip(File srcFile, String destDirPath) throws RuntimeException {
// 判断源文件是否存在
if (!srcFile.exists()) {
throw new BizException(ResultCode.PARAM_ERROR, srcFile.getPath() + "所指文件不存在");
}
// 开始解压
ZipFile zipFile = null;
String zipDir = null;
try {
zipFile = new ZipFile(srcFile);
Enumeration<?> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
if (StringUtils.isBlank(zipDir)) {
zipDir = entry.getName().substring(0, entry.getName().indexOf("/"));
}
// 如果是文件夹,就创建个文件夹
if (entry.isDirectory()) {
String dirPath = destDirPath + "/" + entry.getName();
File dir = new File(dirPath);
dir.mkdirs();
} else {
// 如果是文件,就先创建一个文件,然后用io流把内容copy过去
File targetFile = new File(destDirPath + "/" + entry.getName());
// 保证这个文件的父文件夹必须要存在
targetFile.getParentFile().mkdirs();
// 将压缩文件内容写入到这个文件中
InputStream is = zipFile.getInputStream(entry);
FileOutputStream fos = new FileOutputStream(targetFile);
int len;
byte[] buf = new byte[BUFFER_SIZE];
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
}
// 关流顺序,先打开的后关闭
fos.close();
is.close();
}
}
return destDirPath + File.separator + zipDir;
} catch (Exception e) {
throw new BizException(ResultCode.PARAM_ERROR, "解压文件失败", e);
} finally {
if (zipFile != null) {
try {
zipFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}