Apache Commons IO入門教程

ImportNew - yewenhai發表於2014-11-28

Apache Commons IO是Apache基金會建立並維護的Java函式庫。它提供了許多類使得開發者的常見任務變得簡單,同時減少重複(boiler-plate)程式碼,這些程式碼可能遍佈於每個獨立的專案中,你卻不得不重複的編寫。這些類由經驗豐富的開發者維護,對各種問題的邊界條件考慮周到,並持續修復相關bug。

在下面的例子中,我們會向你演示一些不同功能的方法,這些功能都是在org.apache.commons.io包下。Apache Commons IO 是一個巨大工程,我們不會深入去剖析它的底層原理,但是會演示一些比較常用的例子,不管你是不是新手,相信這些例子都會對你有所幫助。

1. Apache Commons IO 示例

我們分別會用幾段程式碼來演示下面的功能,每部分功能都代表Apache Commons IO所覆蓋的一個單獨領域,具體如下:

  • 工具類
  • 輸入
  • 輸出
  • 過濾器
  • 比較器
  • 檔案監控器

為了更方便讀者進行理解,我們會把建立的每個類的輸出進行單獨展示。並且會把示例中功能演示所用到的到檔案放到工程目錄(ExampleFolder目錄)中。

注意:為了能使用org.apache.commons.io中的功能,你首先需要下載jar包(請點選這裡),並且將jar包新增到Eclipse工程的編譯路徑下,右鍵點工程資料夾 ->  Build Path -> Add external archives。

ApacheCommonsExampleMain.java

public class ApacheCommonsExampleMain {

    public static void main(String[] args) {
        UtilityExample.runExample();

        FileMonitorExample.runExample();

        FiltersExample.runExample();

        InputExample.runExample();

        OutputExample.runExample();

        ComparatorExample.runExample();
    }
}

這個main方法會執行所有的示例,你可以將其他行的程式碼註釋來執行你想要的示例。

1.1 Utility Classes

org.apache.commons.io包中有很多工具類,裡面多數類都是完成檔案操作以及字串比較的功能,下面列舉了一下常用的工具類: FilenameUtils 這個工具類是用來處理檔名(譯者注:包含檔案路徑)的,他可以輕鬆解決不同作業系統檔名稱規範不同的問題(比如windows和Unix)(在Unix系統以及Linux系統中檔案分隔符是“/”,不支援”\“,windows中支援”\“以及”/“)。

FileUtils提供檔案操作(移動檔案,讀取檔案,檢查檔案是否存在等等)的方法。  IOCase提供字串操作以及比較的方法。

FileSystemUtils:提供檢視指定目錄剩餘空間的方法。

UtilityExample.java

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileSystemUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.io.IOCase;

public final class UtilityExample {

    // We are using the file exampleTxt.txt in the folder ExampleFolder,
    // and we need to provide the full path to the Utility classes.
    private static final String EXAMPLE_TXT_PATH =
            "C:UsersLilykosworkspaceApacheCommonsExampleExampleFolderexampleTxt.txt";

    private static final String PARENT_DIR =
            "C:UsersLilykosworkspaceApacheCommonsExample";

    public static void runExample() throws IOException {
        System.out.println("Utility Classes example...");

        // FilenameUtils

        System.out.println("Full path of exampleTxt: " +
                FilenameUtils.getFullPath(EXAMPLE_TXT_PATH));

        System.out.println("Full name of exampleTxt: " +
                FilenameUtils.getName(EXAMPLE_TXT_PATH));

        System.out.println("Extension of exampleTxt: " +
                FilenameUtils.getExtension(EXAMPLE_TXT_PATH));

        System.out.println("Base name of exampleTxt: " +
                FilenameUtils.getBaseName(EXAMPLE_TXT_PATH));

        // FileUtils

        // We can create a new File object using FileUtils.getFile(String)
        // and then use this object to get information from the file.
        File exampleFile = FileUtils.getFile(EXAMPLE_TXT_PATH);
        LineIterator iter = FileUtils.lineIterator(exampleFile);

        System.out.println("Contents of exampleTxt...");
        while (iter.hasNext()) {
            System.out.println("t" + iter.next());
        }
        iter.close();

        // We can check if a file exists somewhere inside a certain directory.
        File parent = FileUtils.getFile(PARENT_DIR);
        System.out.println("Parent directory contains exampleTxt file: " +
                FileUtils.directoryContains(parent, exampleFile));

        // IOCase

        String str1 = "This is a new String.";
        String str2 = "This is another new String, yes!";

        System.out.println("Ends with string (case sensitive): " +
                IOCase.SENSITIVE.checkEndsWith(str1, "string."));
        System.out.println("Ends with string (case insensitive): " +
                IOCase.INSENSITIVE.checkEndsWith(str1, "string."));

        System.out.println("String equality: " +
                IOCase.SENSITIVE.checkEquals(str1, str2));

        // FileSystemUtils
        System.out.println("Free disk space (in KB): " + FileSystemUtils.freeSpaceKb("C:"));
        System.out.println("Free disk space (in MB): " + FileSystemUtils.freeSpaceKb("C:") / 1024);
    }
}

輸出

Utility Classes example...
Full path of exampleTxt: C:UsersLilykosworkspaceApacheCommonsExampleExampleFolder
Full name of exampleTxt: exampleTxt.txt
Extension of exampleTxt: txt
Base name of exampleTxt: exampleTxt
Contents of exampleTxt...
    This is an example text file.
    We will use it for experimenting with Apache Commons IO.
Parent directory contains exampleTxt file: true
Ends with string (case sensitive): false
Ends with string (case insensitive): true
String equality: false
Free disk space (in KB): 32149292
Free disk space (in MB): 31395

1.2 檔案監控器

org.apache.commons.io.monitor包下的類包含的方法可以獲取檔案的指定資訊,不過更重要的是,它可以建立處理器(handler)來跟蹤指定檔案或目錄的變化並且可以在檔案或目錄發生變化的時候進行一些操作。讓我們來看看下面的程式碼:

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileDeleteStrategy;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.apache.commons.io.monitor.FileEntry;

public final class FileMonitorExample {

    private static final String EXAMPLE_PATH =
            "C:UsersLilykosworkspaceApacheCommonsExampleExampleFolderexampleFileEntry.txt";

    private static final String PARENT_DIR =
            "C:UsersLilykosworkspaceApacheCommonsExampleExampleFolder";

    private static final String NEW_DIR =
            "C:UsersLilykosworkspaceApacheCommonsExampleExampleFoldernewDir";

    private static final String NEW_FILE =
            "C:UsersLilykosworkspaceApacheCommonsExampleExampleFoldernewFile.txt";

    public static void runExample() {
        System.out.println("File Monitor example...");

        // FileEntry

        // We can monitor changes and get information about files
        // using the methods of this class.
        FileEntry entry = new FileEntry(FileUtils.getFile(EXAMPLE_PATH));

        System.out.println("File monitored: " + entry.getFile());
        System.out.println("File name: " + entry.getName());
        System.out.println("Is the file a directory?: " + entry.isDirectory());

        // File Monitoring

        // Create a new observer for the folder and add a listener
        // that will handle the events in a specific directory and take action.
        File parentDir = FileUtils.getFile(PARENT_DIR);

        FileAlterationObserver observer = new FileAlterationObserver(parentDir);
        observer.addListener(new FileAlterationListenerAdaptor() {

                @Override
                public void onFileCreate(File file) {
                    System.out.println("File created: " + file.getName());
                }

                @Override
                public void onFileDelete(File file) {
                    System.out.println("File deleted: " + file.getName());
                }

                @Override
                public void onDirectoryCreate(File dir) {
                    System.out.println("Directory created: " + dir.getName());
                }

                @Override
                public void onDirectoryDelete(File dir) {
                    System.out.println("Directory deleted: " + dir.getName());
                }
        });

        // Add a monior that will check for events every x ms,
        // and attach all the different observers that we want.
        FileAlterationMonitor monitor = new FileAlterationMonitor(500, observer);
        try {
            monitor.start();

            // After we attached the monitor, we can create some files and directories
            // and see what happens!
            File newDir = new File(NEW_DIR);
            File newFile = new File(NEW_FILE);

            newDir.mkdirs();
            newFile.createNewFile();

            Thread.sleep(1000);

            FileDeleteStrategy.NORMAL.delete(newDir);
            FileDeleteStrategy.NORMAL.delete(newFile);

            Thread.sleep(1000);

            monitor.stop();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

輸出

File Monitor example...
File monitored: C:UsersLilykosworkspaceApacheCommonsExampleExampleFolderexampleFileEntry.txt
File name: exampleFileEntry.txt
Is the file a directory?: false
Directory created: newDir
File created: newFile.txt
Directory deleted: newDir
File deleted: newFile.tx

讓我們來看看這裡發生了什麼,我們使用org.apache.commons.io.monitor包下的類建立了一個處理器來監聽一些特定的事件(在上面的例子中就是我們對檔案或目錄所做的所有操作事件),為了獲得這些資訊,我們需要做以下幾步操作:

1、建立一個File物件,這個物件指向我們需要監聽變化的目錄。

2、建立一個FileAlterationObserver物件,這個物件會觀察這些變化。

3、通過呼叫addListener()方法,為observer物件新增一個 FileAlterationListenerAdaptor物件。你可以通過很多種方式來建立一個介面卡,在我們的例子中我們使用內部類的方式進行建立並且只實現其中的一部分方法(只需要實現我們例子中需要用的方法即可)。

4、建立一個FileAlterationMonitor 物件,將已經建立好的observer物件新增其中並且傳入時間間隔引數(單位是毫秒)。

5、呼叫start()方法即可開啟監視器,如果你想停止監視器,呼叫stop()方法即可。

1.3 過濾器

過濾器可以以組合的方式使用並且它的用途非常多樣。它可以輕鬆的區分不同的檔案並且找到滿足我們條件的檔案。我們可以組合不同的過濾器來執行檔案的邏輯比較並且精確的獲取我們所需要檔案,而無需使用冗餘的字串比較來尋找我們的檔案。

FiltersExample.java

import java.io.File;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.AndFileFilter;
import org.apache.commons.io.filefilter.NameFileFilter;
import org.apache.commons.io.filefilter.NotFileFilter;
import org.apache.commons.io.filefilter.OrFileFilter;
import org.apache.commons.io.filefilter.PrefixFileFilter;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.io.filefilter.WildcardFileFilter;

public final class FiltersExample {

    private static final String PARENT_DIR =
            "C:UsersLilykosworkspaceApacheCommonsExampleExampleFolder";

    public static void runExample() {
        System.out.println("File Filter example...");

        // NameFileFilter
        // Right now, in the parent directory we have 3 files:
        //      directory example
        //      file exampleEntry.txt
        //      file exampleTxt.txt

        // Get all the files in the specified directory
        // that are named "example".
        File dir = FileUtils.getFile(PARENT_DIR);
        String[] acceptedNames = {"example", "exampleTxt.txt"};
        for (String file: dir.list(new NameFileFilter(acceptedNames, IOCase.INSENSITIVE))) {
            System.out.println("File found, named: " + file);
        }

        //WildcardFileFilter
        // We can use wildcards in order to get less specific results
        //      ? used for 1 missing char
        //      * used for multiple missing chars
        for (String file: dir.list(new WildcardFileFilter("*ample*"))) {
            System.out.println("Wildcard file found, named: " + file);
        }

        // PrefixFileFilter 
        // We can also use the equivalent of startsWith
        // for filtering files.
        for (String file: dir.list(new PrefixFileFilter("example"))) {
            System.out.println("Prefix file found, named: " + file);
        }

        // SuffixFileFilter
        // We can also use the equivalent of endsWith
        // for filtering files.
        for (String file: dir.list(new SuffixFileFilter(".txt"))) {
            System.out.println("Suffix file found, named: " + file);
        }

        // OrFileFilter 
        // We can use some filters of filters.
        // in this case, we use a filter to apply a logical 
        // or between our filters.
        for (String file: dir.list(new OrFileFilter(
                new WildcardFileFilter("*ample*"), new SuffixFileFilter(".txt")))) {
            System.out.println("Or file found, named: " + file);
        }

        // And this can become very detailed.
        // Eg, get all the files that have "ample" in their name
        // but they are not text files (so they have no ".txt" extension.
        for (String file: dir.list(new AndFileFilter( // we will match 2 filters...
                new WildcardFileFilter("*ample*"), // ...the 1st is a wildcard...
                new NotFileFilter(new SuffixFileFilter(".txt"))))) { // ...and the 2nd is NOT .txt.
            System.out.println("And/Not file found, named: " + file);
        }
    }
}

輸出

File Filter example...
File found, named: example
File found, named: exampleTxt.txt
Wildcard file found, named: example
Wildcard file found, named: exampleFileEntry.txt
Wildcard file found, named: exampleTxt.txt
Prefix file found, named: example
Prefix file found, named: exampleFileEntry.txt
Prefix file found, named: exampleTxt.txt
Suffix file found, named: exampleFileEntry.txt
Suffix file found, named: exampleTxt.txt
Or file found, named: example
Or file found, named: exampleFileEntry.txt
Or file found, named: exampleTxt.txt
And/Not file found, named: example

1.4 比較器

使用org.apache.commons.io.comparator 包下的類可以讓你輕鬆的對檔案或目錄進行比較或者排序。你只需提供一個檔案列表,選擇不同的類就可以實現不同方式的檔案比較。

ComparatorExample.java

import java.io.File;
import java.util.Date;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.comparator.LastModifiedFileComparator;
import org.apache.commons.io.comparator.NameFileComparator;
import org.apache.commons.io.comparator.SizeFileComparator;

public final class ComparatorExample {

    private static final String PARENT_DIR =
            "C:UsersLilykosworkspaceApacheCommonsExampleExampleFolder";

    private static final String FILE_1 =
            "C:UsersLilykosworkspaceApacheCommonsExampleExampleFolderexample";

    private static final String FILE_2 =
            "C:UsersLilykosworkspaceApacheCommonsExampleExampleFolderexampleTxt.txt";

    public static void runExample() {
        System.out.println("Comparator example...");

        //NameFileComparator

        // Let's get a directory as a File object
        // and sort all its files.
        File parentDir = FileUtils.getFile(PARENT_DIR);
        NameFileComparator comparator = new NameFileComparator(IOCase.SENSITIVE);
        File[] sortedFiles = comparator.sort(parentDir.listFiles());

        System.out.println("Sorted by name files in parent directory: ");
        for (File file: sortedFiles) {
            System.out.println("t"+ file.getAbsolutePath());
        }

        // SizeFileComparator

        // We can compare files based on their size.
        // The boolean in the constructor is about the directories.
        //      true: directory's contents count to the size.
        //      false: directory is considered zero size.
        SizeFileComparator sizeComparator = new SizeFileComparator(true);
        File[] sizeFiles = sizeComparator.sort(parentDir.listFiles());

        System.out.println("Sorted by size files in parent directory: ");
        for (File file: sizeFiles) {
            System.out.println("t"+ file.getName() + " with size (kb): " + file.length());
        }

        // LastModifiedFileComparator

        // We can use this class to find which file was more recently modified.
        LastModifiedFileComparator lastModified = new LastModifiedFileComparator();
        File[] lastModifiedFiles = lastModified.sort(parentDir.listFiles());

        System.out.println("Sorted by last modified files in parent directory: ");
        for (File file: lastModifiedFiles) {
            Date modified = new Date(file.lastModified());
            System.out.println("t"+ file.getName() + " last modified on: " + modified);
        }

        // Or, we can also compare 2 specific files and find which one was last modified.
        //      returns > 0 if the first file was last modified.
        //      returns  0)
            System.out.println("File " + file1.getName() + " was modified last because...");
        else
            System.out.println("File " + file2.getName() + "was modified last because...");

        System.out.println("t"+ file1.getName() + " last modified on: " +
                new Date(file1.lastModified()));
        System.out.println("t"+ file2.getName() + " last modified on: " +
                new Date(file2.lastModified()));
    }
}

輸出

Comparator example...
Sorted by name files in parent directory: 
    C:UsersLilykosworkspaceApacheCommonsExampleExampleFoldercomparator1.txt
    C:UsersLilykosworkspaceApacheCommonsExampleExampleFoldercomperator2.txt
    C:UsersLilykosworkspaceApacheCommonsExampleExampleFolderexample
    C:UsersLilykosworkspaceApacheCommonsExampleExampleFolderexampleFileEntry.txt
    C:UsersLilykosworkspaceApacheCommonsExampleExampleFolderexampleTxt.txt
Sorted by size files in parent directory: 
    example with size (kb): 0
    exampleTxt.txt with size (kb): 87
    exampleFileEntry.txt with size (kb): 503
    comperator2.txt with size (kb): 1458
    comparator1.txt with size (kb): 4436
Sorted by last modified files in parent directory: 
    exampleTxt.txt last modified on: Sun Oct 26 14:02:22 EET 2014
    example last modified on: Sun Oct 26 23:42:55 EET 2014
    comparator1.txt last modified on: Tue Oct 28 14:48:28 EET 2014
    comperator2.txt last modified on: Tue Oct 28 14:48:52 EET 2014
    exampleFileEntry.txt last modified on: Tue Oct 28 14:53:50 EET 2014
File example was modified last because...
    example last modified on: Sun Oct 26 23:42:55 EET 2014
    exampleTxt.txt last modified on: Sun Oct 26 14:02:22 EET 2014

讓我們來看看這裡用到了哪些類:

NameFileComparator:通過檔名來比較檔案。

SizeFileComparator:通過檔案大小來比較檔案。

LastModifiedFileComparator:通過檔案的最新修改時間來比較檔案。

在這裡你需要注意,比較可以在定的資料夾中(資料夾下的檔案已經被sort()方法排序過了),也可以在兩個指定的檔案之間(通過使用compare()方法)。

1.5 輸入

在org.apache.commons.io.input包下有許多InputStrem類的實現,我們來測試一個最實用的類,TeeInputStream,將InputStream以及OutputStream作為引數傳入其中,自動實現將輸入流的資料讀取到輸出流中。而且,通過傳入第三個引數,一個boolean型別引數,可以在資料讀取完畢之後自動關閉輸入流和輸出流。

InputExample.java

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.input.TeeInputStream;
import org.apache.commons.io.input.XmlStreamReader;

public final class InputExample {

    private static final String XML_PATH =
            "C:UsersLilykosworkspaceApacheCommonsExampleInputOutputExampleFolderweb.xml";

    private static final String INPUT = "This should go to the output.";

    public static void runExample() {
        System.out.println("Input example...");
        XmlStreamReader xmlReader = null;
        TeeInputStream tee = null;

        try {

            // XmlStreamReader

            // We can read an xml file and get its encoding.
            File xml = FileUtils.getFile(XML_PATH);

            xmlReader = new XmlStreamReader(xml);
            System.out.println("XML encoding: " + xmlReader.getEncoding());

            // TeeInputStream

            // This very useful class copies an input stream to an output stream
            // and closes both using only one close() method (by defining the 3rd
            // constructor parameter as true).
            ByteArrayInputStream in = new ByteArrayInputStream(INPUT.getBytes("US-ASCII"));
            ByteArrayOutputStream out = new ByteArrayOutputStream();

            tee = new TeeInputStream(in, out, true);
            tee.read(new byte[INPUT.length()]);

            System.out.println("Output stream: " + out.toString());         
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try { xmlReader.close(); }
            catch (IOException e) { e.printStackTrace(); }

            try { tee.close(); }
            catch (IOException e) { e.printStackTrace(); }
        }
    }
}

輸出

Input example...
XML encoding: UTF-8
Output stream: This should go to the output.

1.6 輸出

與org.apache.commons.io.input包中的類相似, org.apache.commons.io.output包中同樣有OutputStream類的實現,他們可以在多種情況下使用,一個非常有意思的類就是 TeeOutputStream,它可以將輸出流進行分流,換句話說我們可以用一個輸入流將資料分別讀入到兩個不同的輸出流。

OutputExample.java

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.apache.commons.io.input.TeeInputStream;
import org.apache.commons.io.output.TeeOutputStream;

public final class OutputExample {

    private static final String INPUT = "This should go to the output.";

    public static void runExample() {
        System.out.println("Output example...");
        TeeInputStream teeIn = null;
        TeeOutputStream teeOut = null;

        try {

            // TeeOutputStream

            ByteArrayInputStream in = new ByteArrayInputStream(INPUT.getBytes("US-ASCII"));
            ByteArrayOutputStream out1 = new ByteArrayOutputStream();
            ByteArrayOutputStream out2 = new ByteArrayOutputStream();

            teeOut = new TeeOutputStream(out1, out2);
            teeIn = new TeeInputStream(in, teeOut, true);
            teeIn.read(new byte[INPUT.length()]);

            System.out.println("Output stream 1: " + out1.toString());
            System.out.println("Output stream 2: " + out2.toString());

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // No need to close teeOut. When teeIn closes, it will also close its
            // Output stream (which is teeOut), which will in turn close the 2
            // branches (out1, out2).
            try { teeIn.close(); }
            catch (IOException e) { e.printStackTrace(); }
        }
    }
}

輸出

Output example...
Output stream 1: This should go to the output.
Output stream 2: This should go to the output.

2. 下載完整的示例

這是一個Apache Commons IO的入門指導,為開發者介紹了一些可以為你提供輕鬆解決方案的類。在這個龐大的函式庫裡面還有包含很多其他的功能,相信這些例子可以在你未來的專案開發中成為你非常有用工具!

你可以在這裡下載原始碼。

相關文章