程序員的福音 - Apache Commons VFS(上)

此文是系列文章第十篇恭垦,前幾篇請(qǐng)點(diǎn)擊鏈接查看

程序猿的福音 - Apache Commons簡(jiǎn)介

程序員的福音 - Apache Commons Lang

程序員的福音 - Apache Commons IO

程序員的福音 - Apache Commons Codec

程序員的福音 - Apache Commons Compress

程序員的福音 - Apache Commons Exec

程序員的福音 - Apache Commons Email

程序員的福音 - Apache Commons Net

程序員的福音 - Apache Commons Collections

程序員的福音 - Apache Commons HttpClient

Apache Commons VFS 為訪問各種不同的文件系統(tǒng)提供了一個(gè)統(tǒng)一API。支持本地磁盤、HTTP服務(wù)器、FTP服務(wù)器源内、HDFS文件系統(tǒng)油够、ZIP壓縮包等,支持自行擴(kuò)展存儲(chǔ)客戶端鱼响。

commons-vfs 目前最新版本是 2.9.0,最低要求 Java8 以上组底。

maven坐標(biāo)如下:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-vfs2</artifactId>
    <version>2.9.0</version>
</dependency>

下面我將從整體結(jié)構(gòu)丈积,使用方式筐骇,自定義擴(kuò)展等方面簡(jiǎn)單介紹一下。

01. 簡(jiǎn)介

VFS 對(duì)所有文件名都被視為 URI桶癣,這樣就必須使用 %25 對(duì)“%”字符進(jìn)行編碼拥褂。

示例

file:///somedir/some%25file.txt

許多文件系統(tǒng)接受用戶ID和密碼作為 url 的一部分。為了安全性考慮牙寞,Commons VFS 提供了一種加密機(jī)制饺鹃。不過這并不是完全安全的,因?yàn)樵贑ommons VFS 使用密碼之前间雀,還是需要對(duì)密碼進(jìn)行解密的悔详。

創(chuàng)建加密密碼,需要執(zhí)行以下操作:

$ java-cp commons-vfs-2.0.jar org.apache.commons.vfs2.util.EncryptUtil encrypt mypassword

其中 mypassword 是要加密的密碼惹挟。結(jié)果是一行十六進(jìn)制字符串茄螃。例如

$ java-cp commons-vfs-2.0.jar org.apache.commons.vfs2.util.EncryptUtil encrypt WontUBee9

$ D7B82198B272F5C93790FEB38A73C7B8

然后將輸出粘貼到 URL 中,如下所示:

https://testUser:{D7B82198B272F5C93790FEB38A73C7B8}@test.com/test

VFS 將 {} 中包含的密碼視為已加密连锯,并將在使用密碼之前對(duì)其進(jìn)行解密归苍。

Commons VFS 目前支持以下幾種文件系統(tǒng)的訪問

1. 本地文件

提供對(duì)本地物理文件系統(tǒng)上的文件的訪問。

URI格式

[file://]absolute-path

其中絕對(duì)路徑是本地平臺(tái)的有效絕對(duì)文件名运怖。Windows 下支持 UNC 名稱拼弃。

示例

file:///home/someuser/somedir

file:///C:/Documents and Settings

file://///somehost/someshare/afile.txt

/home/someuser/somedir

c:\program files\some dir

c:/program files/some dir

2. Zip, Jar and Tar

提供對(duì)Zip、Jar和Tar文件內(nèi)容的只讀訪問摇展,需要單獨(dú)引入 commons-compress 包吻氧。

URI格式

zip://arch-file-uri[!absolute-path]

jar://arch-file-uri[!absolute-path]

tar://arch-file-uri[!absolute-path]

tgz://arch-file-uri[!absolute-path]

tbz2://arch-file-uri[!absolute-path]

注意:如果您想使用 ! 作為普通字符,必須使用 %21進(jìn)行轉(zhuǎn)義咏连。

tgz 和 tbz2 是 tar:gz 和 tar:bz2 簡(jiǎn)寫形式

示例

jar:../lib/classes.jar!/META-INF/manifest.mf

zip:http://somehost/downloads/somefile.zip

jar:zip:outer.zip!/nested.jar!/somedir

jar:zip:outer.zip!/nested.jar!/some%21dir

tar:gz:http://host/my.tar.gz!/my.tar!/path/in/R.txt

tgz:file://host/dir/my.tgz!/somepath/somefile

3. gzip and bzip2

提供對(duì)gzip和bzip2文件內(nèi)容的只讀訪問盯孙,需要單獨(dú)引入 commons-compress

URI格式

gz://compressed-file-uri

bz2://compressed-file-uri

示例

gz:/my/gz/file.gz

4. HDFS

提供對(duì) apache hadoop 文件系統(tǒng)(HDFS)中文件的讀寫訪問。在 Windows 上祟滴,集成測(cè)試在默認(rèn)情況下是禁用的振惰,因?yàn)樗枰M(jìn)制文件。需要單獨(dú)引入 HDFS 相關(guān)依賴

<dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-hdfs-client</artifactId>
    <version>3.3</version>
    </dependency>
<dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-common</artifactId>
    <version>3.3</version>
</dependency>
<dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-hdfs</artifactId>
    <version>3.3</version>
</dependency>

URI格式

hdfs://hostname[:port][absolute-path]

示例

hdfs://somehost:8080/downloads/some_dir

hdfs://somehost:8080/downloads/some_file.ext

5. HTTP and HTTPS

提供對(duì) HTTP 服務(wù)器上文件的訪問垄懂,需要依賴 HttpClient骑晶,同時(shí)支持HttpClient 3,HttpClient 4埠偿,HttpClient 5 版本,只需要引入其中一個(gè)版本的依賴即可榜晦。

URI格式

http://[username[:password]@]hostname[:port][absolute-path]

https://[username[:password]@]hostname[:port][absolute-path]

額外可選參數(shù)

proxyHost:要連接的代理主機(jī)冠蒋。

proxyPort:要使用的代理端口。

proxyScheme:要使用的代理方案(http/https)乾胶。

cookies:要添加到請(qǐng)求的 cookies 數(shù)組抖剿。

maxConnectionsPerHost:允許連接到特定主機(jī)和端口的最大連接數(shù)朽寞。默認(rèn)值為5。

maxTotalConnections:所有主機(jī)允許的最大連接數(shù)斩郎。默認(rèn)值為50脑融。

keystoreFile:SSL連接的密鑰庫(kù)文件。

keystorePass:密鑰庫(kù)密碼缩宜。

keystoreType:密鑰庫(kù)類型肘迎。

示例

http://somehost:8080/downloads/somefile.jar

http://myusername@somehost/index.html

6. WebDAV

通過 commons-vfs2-jackrabbit1 和 commons-vfs2-jackrabbit2 模塊提供對(duì) WebDAV 服務(wù)器上文件的訪問。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-vfs2-jackrabbit1</artifactId>
    <version>2.9.0</version>
</dependency>

URI格式

webdav://[username[:password]@]hostname[:port][absolute-path]

額外可選參數(shù)

versioning :如果應(yīng)啟用版本控制锻煌,則版本控制為 true

creatorName:要通過更改文件來標(biāo)識(shí)的用戶名妓布。如果未設(shè)置,將使用用于身份驗(yàn)證的用戶名宋梧。

示例

webdav://somehost:8080/dist

7. FTP and FTPS

提供對(duì)FTP服務(wù)器上文件的訪問匣沼。依賴 commons-net

URI格式

ftp://[user[:pass]@]host[:port][relative-path]

示例

ftp://user:pass@somehost/pub/somefile.tgz

默認(rèn)情況下,路徑相對(duì)于用戶的主目錄捂龄∈吞危可通過以下方式進(jìn)行更改:

FtpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(options, false);

8. SFTP

提供對(duì) SFTP 服務(wù)器(即 SSH 或 SCP 服務(wù)器)上的文件的訪問。需要添加如下依賴

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>

URI格式

sftp://[user[:pass]@]hostname[:port][relative-path]

示例

sftp://user:pass@somehost/pub/file.tgz

默認(rèn)情況下倦沧,路徑相對(duì)于用戶的主目錄唇撬。可通過以下方式進(jìn)行更改:

FtpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(options, false);

9. Temporary Files

提供對(duì)臨時(shí)文件系統(tǒng)的訪問刀脏,該文件系統(tǒng)在 Commons VFS 關(guān)閉時(shí)被刪除局荚。臨時(shí)文件系統(tǒng)由本地文件系統(tǒng)支持

URI格式

tmp://[absolute-path]

示例

tmp://dir/somefile.txt

10. RAM

在內(nèi)存中存儲(chǔ)所有數(shù)據(jù)的文件系統(tǒng)(每個(gè)文件內(nèi)容一個(gè)字節(jié)數(shù)組)

URI格式

ram://[path]

額外可選參數(shù)

maxsize:最大文件系統(tǒng)大小(所有文件內(nèi)容的總字節(jié)數(shù))

示例

ram:///any/path/to/file.txt

11. Resource

這實(shí)際上不是一個(gè)文件系統(tǒng)愈污,它使用 ClassLoader.getResource() 查找資源耀态,并創(chuàng)建一個(gè) VFS url 以供進(jìn)一步處理

URI格式

res://[path]

示例

res://path/in/image.png

將會(huì)轉(zhuǎn)換為 ->

jar:file://my/path/images.jar!/path/in/image.png

12. CIFS

提供對(duì) CIFS 服務(wù)器(如 Samba服務(wù) 或 Windows共享)的訪問。

官方還在開發(fā)中(截止本文發(fā)布時(shí)間2021-08-22)

URI格式

smb://[username[:password]@]hostname[:port][absolute-path]

示例

smb://somehost/home

13. MIME

可以讀取郵件及其附件暂雹,比如歸檔文件首装。如果已解析郵件中的某個(gè)部分沒有名稱,則將生成一個(gè)偽名稱杭跪。虛擬名稱是:_body_part_X仙逻,其中X將被零件號(hào)替換。

官方還在開發(fā)中(截止本文發(fā)布時(shí)間2021-08-22)

URI格式

mime://mime-file-uri[!absolute-path]

示例

mime:file:///your/path/mail/anymail.mime!/

mime:file:///your/path/mail/anymail.mime!/filename.pdf

mime:file:///your/path/mail/anymail.mime!/_body_part_0

14. 自定義擴(kuò)展

Commons VFS 接口支持開閉原則涧尿,允許開發(fā)者在不改變?cè)蓄惖幕A(chǔ)上自行擴(kuò)展自己的實(shí)現(xiàn)系奉,如亞馬遜S3文件,阿里云文件等姑廉。只需在類路徑下創(chuàng)建"META-INF/vfs-providers.xml"文件缺亮,添加額外的配置并編寫類實(shí)現(xiàn) VFS 對(duì)應(yīng)的接口。具體擴(kuò)展方式將在下章節(jié)做介紹桥言。

02. 整體結(jié)構(gòu)

FileSystemManager:管理一組文件系統(tǒng)萌踱。此接口用于按名稱從這些文件系統(tǒng)之一中定位獲取 org.apache.commons.vfs2.FileObject 葵礼。

FileProvider:文件提供者。每個(gè)文件提供者負(fù)責(zé)處理特定 URI 的文件并鸵。

FileNameParser:提供將文件名解析為 org.apache.commons.vfs2.FileName 的方法鸳粉。

FileSystem:一個(gè)文件系統(tǒng),由文件的層次結(jié)構(gòu)組成园担。

FileObject:代表一個(gè)文件届谈,用于訪問文件的內(nèi)容和結(jié)構(gòu)。有兩種類型的文件:文件夾粉铐,包含其他文件疼约;普通文件,包含數(shù)據(jù)或內(nèi)容蝙泼。一個(gè)文件夾可能沒有任何內(nèi)容程剥,普通文件不能包含其他文件。

FilesCache:文件緩存接口汤踏。VFS 內(nèi)置一下幾種實(shí)現(xiàn)织鲸,SoftRefFilesCache(軟引用,默認(rèn)值)溪胶,WeakRefFilesCache(弱引用)搂擦,DefaultFilesCache(沒有過期和限制),LRUFilesCache(LRU實(shí)現(xiàn)哗脖,默認(rèn)容量100個(gè))瀑踢,NullFilesCache(空實(shí)現(xiàn),不做任何緩存)才避。

FileSelector:該接口用于查找子文件時(shí)定義選擇規(guī)則橱夭,使用方式 FileObject.findFiles(FileSelector) 。VFS 內(nèi)置了 7 種選擇器桑逝。

VFS 加載文件流程大致如下:

FileSystemManager 解析文件名棘劣,通過文件名中的協(xié)議(如ftp://中的ftp)獲取對(duì)應(yīng) FileProvider 對(duì)象,F(xiàn)ileProvider 通過 FileNameParser 對(duì)象解析文件名獲取對(duì)應(yīng)的 FileSystem 對(duì)象楞遏,通過 FileSystem 對(duì)象的 resolveFile 方法獲取文件(默認(rèn)先從緩存中查找茬暇,不存在再調(diào)用 createFile 方法創(chuàng)建 FileObject 對(duì)象,F(xiàn)ileObject 就是實(shí)體文件的抽象寡喝,提供讀取和修改等相關(guān)能力)

主要類圖結(jié)構(gòu)如下

image
image
image
image
image

03. 使用方式

1. 文件屬性

// 獲取默認(rèn)FileSystemManager
FileSystemManager fsMgr = VFS.getManager();
String path = "D:/test/abc";
// 獲取的是 LocalFileObject 實(shí)現(xiàn)
FileObject fo = fsMgr.resolveFile(path);
println(fo.getFileSystem()); // LocalFileSystem
if (!fo.exists()) {
    println("fo not exists");
    return;
}
println("parent:"+fo.getParent().toString());// "file:///D:/"
println("name:"+fo.getName());// "file:///D:/test"
println("path:"+fo.getPath());// "D:\test"
println("pubURI:"+fo.getPublicURIString());// "file:///D:/test"
println("URI:"+fo.getURI().toString());// "file:///D:/test"
println("URL:"+fo.getURL());// "file:///D:/test"
boolean isFile = fo.isFile();
boolean isFolder = fo.isFolder();
// 是否符號(hào)鏈接
boolean isSymbolic = fo.isSymbolicLink();
boolean executable = fo.isExecutable();
boolean isHidden = fo.isHidden();
println("type:"+fo.getType());
// 應(yīng)該放到finally塊中關(guān)閉糙俗,為了便于閱讀直接在此關(guān)閉了
// 會(huì)同時(shí)關(guān)閉FileContent并釋放FileObject
fo.close();
// 關(guān)閉文件系統(tǒng),釋放連接预鬓,清除緩存等
fsMgr.close();

2. 讀取文件內(nèi)容

// 讀取文件內(nèi)容
// 支持獲取字符串巧骚,流,字節(jié)數(shù)組等
FileSystemManager fsMgr = VFS.getManager();
String path = "E:\\yuanzhy\\yyhc.py";
FileObject fo = fsMgr.resolveFile(path);
if (fo.isFile()) {
    FileContent fc = fo.getContent();
    // fc.getInputStream();
    // fc.getByteArray();
    // 獲取內(nèi)容 - 字符串形式
    String content = fc.getString("UTF-8");
    println(content);
}
// 在finally或者try-resources中關(guān)閉資源
fo.close();
fsMgr.close();

3. 讀取文件屬性

讀取文件只讀的屬性信息

只有本地 Jar文件 和 HDFS文件 支持此功能,其他類型文件獲取的為空

jar 屬性就是 manifest 中的屬性

HDFS 支持的屬性都在 HdfsFileAttributes 枚舉中

訪問時(shí)間:HdfsFileAttributes.LAST_ACCESS_TIME

塊大型臁:HdfsFileAttributes.BLOCK_SIZE

用戶組:HdfsFileAttributes.GROUP

所有者:HdfsFileAttributes.OWNER

操作權(quán)限:HdfsFileAttributes.PERMISSIONS

文件大小:HdfsFileAttributes.LENGTH

修改時(shí)間:HdfsFileAttributes.MODIFICATION_TIME

FileSystemManager fsMgr = VFS.getManager();
String path = "D:/test/a.txt";
FileObject fo = fsMgr.resolveFile(path);
if (fo.isFile()) {
    FileContent fc = fo.getContent();
    // 獲取只讀的文件屬性
    Map<String, Object> attrs = fc.getAttributes();
    println(fc.getAttributes());
}
// 在finally或者try-resources中關(guān)閉資源
fo.close();
fsMgr.close();

4. 設(shè)置文件權(quán)限

設(shè)置文件屬性蟋定,如可讀可寫可執(zhí)行等粉臊,只有 本地文件 和 SFTP 上的文件支持此功能

FileSystemManager fsMgr = VFS.getManager();
String path = "D:/test/a.txt";
FileObject fo = fsMgr.resolveFile(path);
if (fo.isFile()) {
    fo.setWritable(true, true);
    fo.setExecutable(true, true);
}
// 在finally或者try-resources中關(guān)閉資源
fo.close();
fsMgr.close();

5. 讀取目錄

// 主要是獲取子文件
FileSystemManager fsMgr = VFS.getManager();
String path = "D:/test";
FileObject fo = fsMgr.resolveFile(path);
if (fo.isFolder()) {
    // 獲取所有子文件
    FileObject[] foArr = fo.getChildren();
    // 獲取子文件(名稱為test)
    FileObject test = fo.getChild("a.txt");
    // 從所有后代中獲取類型是文件的文件
    FileObject[] files = fo.findFiles(Selectors.SELECT_FILES);
}
// 在finally或者try-resources中關(guān)閉資源
fo.close();
fsMgr.close();

6. 刪除

目錄可通過參數(shù)刪除部分子文件。只有本地文件驶兜、內(nèi)存文件扼仲、FTP、SFTP抄淑、HDFS支持刪除

FileSystemManager fsMgr = VFS.getManager();
String path = "D:/test/a.txt";
FileObject fo = fsMgr.resolveFile(path);
if (fo.isFolder()) {
    // 刪除此文件和所有子文件, 返回刪除的數(shù)量
    fo.deleteAll(); // 同fo.delete(Selectors.SELECT_ALL);
    // 只刪除所有子文件
    fo.delete(Selectors.EXCLUDE_SELF);
    // 只刪除直接子文件和空目錄
    fo.delete(Selectors.SELECT_CHILDREN);
    // 只刪除文件
    fo.delete(Selectors.SELECT_FILES);
    // 只刪除空的子目錄
    fo.delete(Selectors.SELECT_FOLDERS);
    // 刪除目錄本身(如果包含子文件則刪除失敗返回0)
    fo.delete(Selectors.SELECT_SELF);
    // 目錄不為空則刪除失敗返回false
    boolean suc = fo.delete();
} else if (fo.isFile()) {
    // 刪除文件本身
    boolean suc = fo.delete();
}
// 在finally或者try-resources中關(guān)閉資源
fo.close();
fsMgr.close();

7. 拷貝文件

// 將文件內(nèi)容寫入其他地方
FileSystemManager fsMgr = VFS.getManager();
String path = "D:/test/a.txt";
FileObject fo = fsMgr.resolveFile(path);
if (fo.isFile()) {
    FileContent fc = fo.getContent();
    // 支持寫入輸出流屠凶,F(xiàn)ileContent和FileObject中
    // fc.write(FileObject);
    // fc.write(FileContent);
    // 相當(dāng)于下載到 D:/test/a.txt
    fc.write(new FileOutputStream("E:/test/a.txt"));
}
// 在finally或者try-resources中關(guān)閉資源
fo.close();
fsMgr.close();

8. 修改文件

新增or修改內(nèi)容。只有本地文件肆资、內(nèi)存文件矗愧、FTP、SFTP郑原、GZip唉韭、bz2、HDFS支持犯犁,其中只有本地文件属愤、內(nèi)存文件、FTP酸役、SFTP支持追加寫住诸,GZip、bz2涣澡、HDFS 只支持覆蓋寫贱呐。

FileSystemManager fsMgr = VFS.getManager();
String path = "D:/test/a.txt";
FileObject fo = fsMgr.resolveFile(path);
fo.createFile();
if (fo.isFile()) {
    FileContent fc = fo.getContent();
    OutputStream os = fc.getOutputStream();
    if (fo.isWriteable()) {
        // 覆蓋寫入
        IOUtils.write("測(cè)試", os, "UTF-8");
        os.close();
    }
    try {
        // 追加寫
        os = fc.getOutputStream(true);
        IOUtils.write("追加數(shù)據(jù)", os, "UTF-8");
    } catch (FileSystemException e) {
        // 不支持追加寫入
        System.err.println("不支持追加寫入");
    }
}
// 在finally或者try-resources中關(guān)閉資源
fo.close();
fsMgr.close();

9. 隨機(jī)讀寫

隨機(jī)只讀:本地文件、內(nèi)存文件暑塑、FTP吼句、SFTP、HDFS事格、HTTP支持

隨機(jī)讀寫:本地文件惕艳、內(nèi)存文件支持

FileSystemManager fsMgr = VFS.getManager();
String path = "D:/test/a.txt";
FileObject fo = fsMgr.resolveFile(path);
if (fo.isFile()) {
    FileContent fc = fo.getContent();
    try {
        RandomAccessContent rac = fc.getRandomAccessContent(RandomAccessMode.READ);
        // ... ...
    } catch (FileSystemException e) {
        // 不支持RandomAccessMode.READ
    }
}
// 在finally或者try-resources中關(guān)閉資源
fo.close();
fsMgr.close();

10. 更改緩存實(shí)現(xiàn)

StandardFileSystemManager fsMgr = new StandardFileSystemManager();

// 手動(dòng)處理緩存數(shù)據(jù)。調(diào)用 FileObject#refresh() 來刷新對(duì)象數(shù)據(jù)
fsMgr.setCacheStrategy(CacheStrategy.MANUAL);
// 每次從 FileSystemManager#resolveFile 請(qǐng)求文件時(shí)刷新數(shù)據(jù)驹愚。
// fsMgr.setCacheStrategy(CacheStrategy.ON_RESOLVE);
// 每次在 fileObject 上調(diào)用方法時(shí)刷新數(shù)據(jù)愚战。僅當(dāng)您確實(shí)需要最新信息時(shí)才使用此設(shè)置,因?yàn)榇嗽O(shè)置會(huì)造成重大性能損失把跨。
// fsMgr.setCacheStrategy(CacheStrategy.ON_CALL);

// 設(shè)置緩存實(shí)現(xiàn)為L(zhǎng)RU
fsMgr.setFilesCache(new LRUFilesCache());
fsMgr.init();
FileObject fo = fsMgr.resolveFile("D:/test/a.txt");
// ... ... 

11. 文件監(jiān)聽

監(jiān)聽文件創(chuàng)建们拙,修改或刪除

public class ListenersDemo {
    @Test
    public void test() throws IOException {
        // 監(jiān)聽文件創(chuàng)建,修改或刪除
        FileSystemManager fsMgr = VFS.getManager();
        String path = "D:/test/a.txt";
        FileObject fo = fsMgr.toFileObject(new File(path));
        // 添加監(jiān)聽器
        fo.getFileSystem().addListener(fo, new MyListener());
        if (!fo.exists()) {
            fo.createFile();
        }
        fo.setWritable(false, false);
//        fo.delete();
        fo.close();
        fsMgr.close();
    }

    private class MyListener implements FileListener {
        @Override
        public void fileCreated(FileChangeEvent event) throws Exception {
            println("fileCreated:"+event.getFileObject().getName());
        }
        @Override
        public void fileDeleted(FileChangeEvent event) throws Exception {
            println("fileDeleted:"+event.getFileObject().getName());
        }
        @Override
        public void fileChanged(FileChangeEvent event) throws Exception {
            println("fileChanged:"+event.getFileObject().getName());
        }
    }
}

04. 總結(jié)

Commons VFS 擁有統(tǒng)一的文件系統(tǒng)訪問 API,使用一套代碼可以輕松的實(shí)現(xiàn)不同文件系統(tǒng)的讀寫操作倘潜,對(duì)于一些場(chǎng)景(比如同時(shí)支持多種存儲(chǔ)系統(tǒng)绷柒,或者存儲(chǔ)系統(tǒng)可以根據(jù)部署情況動(dòng)態(tài)配置)來說還是非常實(shí)用的,由于其接口的抽象性設(shè)計(jì)可以非常方便的擴(kuò)展以支持其他文件系統(tǒng)涮因,有對(duì)應(yīng)需求可以考慮使用废睦。

由于篇幅原因,關(guān)于自定義擴(kuò)展的方法我將在下一篇中再給大家做詳細(xì)講解养泡。

后續(xù)章節(jié)我將繼續(xù)給大家介紹commons中其他好用的工具類庫(kù)嗜湃,期待你的關(guān)注。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末澜掩,一起剝皮案震驚了整個(gè)濱河市购披,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肩榕,老刑警劉巖刚陡,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異株汉,居然都是意外死亡橘荠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門郎逃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哥童,“玉大人,你說我怎么就攤上這事褒翰≈福” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵优训,是天一觀的道長(zhǎng)朵你。 經(jīng)常有香客問我,道長(zhǎng)揣非,這世上最難降的妖魔是什么抡医? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮早敬,結(jié)果婚禮上忌傻,老公的妹妹穿的比我還像新娘。我一直安慰自己搞监,他們只是感情好水孩,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著琐驴,像睡著了一般俘种。 火紅的嫁衣襯著肌膚如雪秤标。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天宙刘,我揣著相機(jī)與錄音苍姜,去河邊找鬼。 笑死悬包,一個(gè)胖子當(dāng)著我的面吹牛怖现,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播玉罐,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼潘拨!你這毒婦竟也來了吊输?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤铁追,失蹤者是張志新(化名)和其女友劉穎季蚂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琅束,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扭屁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涩禀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片料滥。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖艾船,靈堂內(nèi)的尸體忽然破棺而出葵腹,到底是詐尸還是另有隱情,我是刑警寧澤屿岂,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布践宴,位于F島的核電站,受9級(jí)特大地震影響爷怀,放射性物質(zhì)發(fā)生泄漏阻肩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一运授、第九天 我趴在偏房一處隱蔽的房頂上張望烤惊。 院中可真熱鬧,春花似錦吁朦、人聲如沸撕氧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)伦泥。三九已至剥啤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間不脯,已是汗流浹背府怯。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留防楷,地道東北人牺丙。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像复局,于是被迫代替她去往敵國(guó)和親冲簿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容