当前位置 : 首页 » 文章分类 :  开发  »  Java-File

Java-File

Java 文件操作相关笔记
Java 中 File 可以代表文件或目录。


SpringBoot 图片下载接口示例

@Slf4j
@Api(tags = "图片接口")
@RestController
public class ImageController {
    @ApiOperation("图片读取接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "filename", value = "图片文件名", example = "example.png")
    })
    @GetMapping(value = "/api/image")
    public void imageDownload(HttpServletResponse response, @RequestParam("filename") String filename) {
        log.info("Enter method imageDownload, filename: {}", filename);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.IMAGE_JPEG_VALUE);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;fileName=" + filename);
        try (
                // 从 classpath 下读取图片
                InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(filename);
                OutputStream outputStream = response.getOutputStream();
        ) {
            IOUtils.copy(inputStream, outputStream);
        } catch (IOException e) {
            log.error("图片读取失败");
        }
        log.info("End method imageDownload");
    }
}

InputStream

FileInputStream 无法重复读

InputStream 是无法重复读取的。

如下单测,两次计算文件输入流的 md5 值不同,因为第一遍读完 FileInputStream 后,就是个空的 InputStream 了。
第一次是计算完整输入流的 md5,第二次是计算一个空的输入流的 md5。把第二次和第三次结果对比,发现第二次的 md5 结果其实就是空字符串的 md5。

@Test
public void testMd5() throws Exception {
    File file = new File("/Users/xx/git/my/hexo/source/_posts/Java/Java-Math.md");
    InputStream inputStream = new FileInputStream(file);
    log.info(DigestUtils.md5Hex(inputStream));
    log.info(DigestUtils.md5Hex(inputStream));
    log.info(DigestUtils.md5Hex(""));
}

结果:
bc71dce15c32ff0dea6636be3aeab5ee
d41d8cd98f00b204e9800998ecf8427e
d41d8cd98f00b204e9800998ecf8427e

曾经犯的一个错就是在按行读取文件流后,再用这个 InputStream 计算 md5 值,结果发现全部文件的 md5 都相同,都是 d41d8cd98f00b204e9800998ecf8427e,排查了一会儿才发现读的都是空的输入流的 md5,也就是空字符串的 md5。


File

java.io.File

public class File
extends Object
implements Serializable, Comparable<File>{}

separator 路径分隔符

File.separator Linux 上是 /,Windows 上是 \\


getName() 获取文件名(无路径有扩展名)

public String getName()
获取不带路径的文件名,包含扩展名。


getPath() 返回路径名

public String getPath()
返回的是定义时的路径,可能是相对路径,也可能是绝对路径,这个取决于定义时用的是相对路径还是绝对路径。
如果定义时用的是绝对路径,那么使用 getPath() 返回的结果跟用 getAbsolutePath() 返回的结果一样。
getPath() 返回的是 File 构造方法里的路径,是什么就是什么,不增不减。

getAbsolutePath() 返回绝对路径

public String getAbsolutePath()

返回此抽象路径名的绝对路径名字符串。
如果此抽象路径名已经是绝对路径名,则返回该路径名字符串,这与 getPath() 方法一样。
如果此抽象路径名是空抽象路径名,则返回当前用户目录的路径名字符串,该目录由系统属性 user.dir 指定。
否则,使用与系统有关的方式解析此路径名。
在 UNIX 系统上,根据当前用户目录解析相对路径名,可使该路径名成为绝对路径名。
在 Microsoft Windows 系统上,根据路径名指定的当前驱动器目录(如果有)解析相对路径名,可使该路径名成为绝对路径名;否则,可以根据当前用户目录解析它。

getAbsolutePath() 不会处理路径中的 ...

getCanonicalPath() 返回规范化绝对路径

public String getCanonicalPath() throws IOException
返回的是规范化的绝对路径,等价于先调用 getAbsolutePath() 获取绝对路径再将 ... 解析成对应的正确的路径

获取文件扩展名

File file = new File("/tmp/file/aaa.xlsx");
String extension = file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf("."));

结果为 “.xlsx”


exists() 判断文件或目录是否存在

public boolean exists()
检查 File 代表的文件路径是否是已经存在的文件或目录。若已存在,返回 true


listFiles(FileFilter filter) 根据filter筛选文件

返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
除了返回数组中的路径名必须满足过滤器外,此方法的行为与 listFiles() 方法相同。
如果给定 filter 为 null,则接受所有路径名。否则,当且仅当在路径名上调用过滤器的 FileFilter.accept(java.io.File) 方法返回 true 时,该路径名才满足过滤器。
参数:filter - 文件过滤器
返回:抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件和目录。如果目录为空,那么数组也将为空。如果抽象路径名不表示一个目录,或者发生 I/O 错误,则返回 null。
抛出: SecurityException - 如果存在安全管理器,且其 SecurityManager.checkRead(java.lang.String) 方法拒绝对目录进行读访问

public File[] listFiles(FileFilter filter) 接受一个 FileFilter 文件过滤器参数
FileFilter 本身是一个函数式接口,可以直接用一个lambda表达式代替,例如:
筛选目录下的所有 .xlsx 文件
File[] fileArray = dir.listFiles(file -> file.getName().toLowerCase().endsWith(".xlsx"));

FileFilter 源码:

package java.io;

@FunctionalInterface
public interface FileFilter {

    /**
     * Tests whether or not the specified abstract pathname should be
     * included in a pathname list.
     *
     * @param  pathname  The abstract pathname to be tested
     * @return  <code>true</code> if and only if <code>pathname</code>
     *          should be included
     */
    boolean accept(File pathname);
}

java 8 lambda expression for FilenameFilter
https://stackoverflow.com/questions/29316310/java-8-lambda-expression-for-filenamefilter

java.io.File
https://docs.oracle.com/javase/9/docs/api/java/io/File.html


按文件名过滤目录中的文件列表(lambda表达式)

只列出目录中的扩展名为.xlsx的文件

private void filtFiles() {
    String fileDir = "/tmp/files/";
    File dir = new File(fileDir);
    if (!dir.exists()) {
        logger.info("Dir {} does not exist", fileDir);
        return;
    }

    File[] fileArray = dir.listFiles(file -> file.getName().toLowerCase().endsWith(".xlsx"));
    if (fileArray.length == 0) {
        logger.info("Dir {} doesn't have xlsx files", fileDir);
        return;
    }

    for (File file : fileArray) {
      System.out.println(file.getName());
    }
}

createTempFile() 创建临时文件

File tempFile = File.createTempFile("temp", ".png");
File tempFile = File.createTempFile("temp", ".txt");

示例

用ClassLoader读取classpath下的文件

类 ClassLoader 有个 getResource 方法,用以classpath下的资源
public URL getResource(String name)
查找具有给定名称的资源。资源是可以通过类代码以与代码基无关的方式访问的一些数据(图像、声音、文本等)。
资源名称是以 ‘/‘ 分隔的标识资源的路径名称。
此方法首先搜索资源的父类加载器;如果父类加载器为 null,则搜索的路径就是虚拟机的内置类加载器的路径。如果搜索失败,则此方法将调用 findResource(String) 来查找资源。
参数: name - 资源名称
返回:读取资源的 URL 对象;如果找不到该资源,或者调用者没有足够的权限获取该资源,则返回 null。

读取jar包中classpath内文件

this.getClass().getClassLoader().getResource(“file.txt”) 读取classpath中文件的方式在 idea 中本地运行是没问题的,但如果打成jar包运行就会读取不到文件,此时必须使用 getResourceAsStream() 方法才行

两种方法的示例如下:

package com.masikkk.blog.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import org.apache.commons.io.IOUtils;
import org.junit.Test;

public class FileTest {
    // 从Jar包中的classpath下读取文件
    @Test
    public void readClasspathFileInJar() throws Exception {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("file.txt");
        IOUtils.readLines(inputStream).forEach(System.out::println);
    }

    // 从classpath下读取文件,打成Jar包后此方法读不到文件
    @Test
    public void readClasspathFile() throws Exception {
        URL url = this.getClass().getClassLoader().getResource("file.txt");
        System.out.println("绝对路径文件名: " + url.getFile());
        File f = new File(url.getFile());
        InputStream inputStream = new FileInputStream(f);
        IOUtils.readLines(inputStream).forEach(System.out::println);
    }
}

如果想转为 File,可以

InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("xxx.txt");
File tempFile = File.createTempFile("temp", ".txt");
FileUtils.copyInputStreamToFile(inputStream, tempFile);

BufferedWriter写文件

File file = new File("/Users/xxxx/xxxx.txt");
if (!file.exists()) {
    try {
      file.createNewFile();
    } catch (IOException e) {
      e.printStackTrace();
    }
    logger.info("Create local file: {}", file.getAbsolutePath());
}
BufferedWriter out = new BufferedWriter(new FileWriter(file));
out.write("写入文件\r\n"); // \r\n即为换行
out.flush(); // 把缓存区内容压入文件
out.close(); // 最后记得关闭文件

FileWriter文件覆盖和追加

在实际写入文件时,有两种写入文件的方式:覆盖和追加。
“覆盖”是指清除原文件的内容,写入新的内容,默认采用该种形式写文件,
“追加”是指在已有文件的末尾写入内容,保留原来的文件内容,例如写日志文件时,一般采用追加。

在实际使用时可以根据需要采用适合的形式,可以使用: public FileOutputStream(String name, boolean append) throws FileNotFoundException 只需要使用该构造方法在构造 FileOutputStream 对象时,将第二个参数 append 的值设置为 true 即可。

覆盖:

try {
   BufferedWriter out = new BufferedWriter(new FileWriter("outfilename"));
   out.write("aString");
   out.close();
} catch (IOException e) {
}

追加:

try {
   BufferedWriter out = new BufferedWriter(new FileWriter("filename", true));
   out.write("aString");
   out.close();
} catch (IOException e) {
}

Java追加内容到文件末尾的方法

方法一、使用FileOutputStream,在构造FileOutputStream时,把第二个参数append设为true

BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));
bufferedWriter.write(conent);
bufferedWriter.close();

二、打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件

FileWriter writer = new FileWriter(fileName, true);
writer.write(content);
writer.close();

三、打开一个随机访问文件流,按读写方式

RandomAccessFile randomFile = new RandomAccessFile(fileName, "rw");
// 文件长度,字节数
long fileLength = randomFile.length();
// 将写文件指针移到文件尾。
randomFile.seek(fileLength);
randomFile.writeBytes(content);
randomFile.close();

java 追加内容到文件末尾的几种常用方法
https://blog.csdn.net/jsjwk/article/details/3942167


BufferedWriter 逐行追加写入文件

/**
 * 将行数据写入File
 * @param rows
 * @param append 是否追加
 */
private void writeFile(List<String> rows, boolean append) throws IOException {
    // 创建目录
    File dir = new File(FILE_DIR);
    if (!dir.exists()) {
        dir.mkdirs();
        logger.info("Create local dir: {}", FILE_DIR);
    }

    // 计算文件名
    String fileName = String.format(FILE_NAME, DateTimeUtils.toDate(new Date()));
    File file = new File(FILE_DIR + fileName);

    // 新建文件
    if (!file.exists()) {
        file.createNewFile();
        logger.info("Create local file: {}", file.getAbsolutePath());
    }

    BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), "utf-8"));

    // 写入文件
    for (String row : rows) {
        bufferedWriter.write(row);
        bufferedWriter.newLine();
    }
    bufferedWriter.close();
}

Java BufferedReader 读入文件到List

File file = new File("/xx/xx.txt");
if (file != null && file.exists() && file.isFile()) {
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "utf-8"));
    // 第一行是表头
    bufferedReader.readLine();
    String lineStr = bufferedReader.readLine();
    while (StringUtils.isNotEmpty(lineStr)) {
        updatedUuidList.add(Long.valueOf(lineStr));
        lineStr = bufferedReader.readLine();
    }
    bufferedReader.close();
}

WatchService

WatchService 可以实时的监控操作系统中文件的变化,包括创建、更新和删除事件。
WatchService 类似于在观察者模式中的观察者,Watchable 类似域观察者模式中的被观察者。

监听用户家目录的文件变动,打印相应的信息

public class DirectoryWatcherExample {

    public static void main(String[] args) {
        WatchService watchService
          = FileSystems.getDefault().newWatchService();

        Path path = Paths.get(System.getProperty("user.home"));

        path.register(
          watchService,
            StandardWatchEventKinds.ENTRY_CREATE,
              StandardWatchEventKinds.ENTRY_DELETE,
                StandardWatchEventKinds.ENTRY_MODIFY);

        WatchKey key;
        while ((key = watchService.take()) != null) {
            for (WatchEvent<?> event : key.pollEvents()) {
                System.out.println(
                  "Event kind:" + event.kind()
                    + ". File affected: " + event.context() + ".");
            }
            key.reset();
        }
    }
}

上一篇 关于

下一篇 Apache-POI

阅读
评论
2.7k
阅读预计12分钟
创建日期 2019-01-04
修改日期 2023-06-12
类别
标签

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论