vfs 虚拟文件系统
Commons VFS 支持文件系统
1.本地文件
- [file://]absolute-path
2. Zip, Jar and Tar
- [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]
3. gzip and bzip2
4. HDFS
- [hdfs://hostname:port][absolute-path]
5. HTTP and HTTPS
- http://[username[:password]@]hostname[:port][absolute-path]
- https://[username[:password]@]hostname[:port][absolute-path]
6. WebDAV
- webdav://[username[:password]@]hostname[:port][absolute-path]
通过 commons-vfs2-jackrabbit1 和 commons-vfs2-jackrabbit2 模块提供对 WebDAV 服务器上文件的访问。
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-vfs2-jackrabbit1</artifactId>
<version>2.9.0</version>
</dependency>
7. FTP and FTPS
- ftp://[user[:pass]@]host[:port][relative-path]
// 默认情况下,路径相对于用户的主目录。可通过以下方式进行更改:
FtpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(options, false);
8. SFTP
- sftp://[user[:pass]@]hostname[:port][relative-path]
提供对 SFTP 服务器(即 SSH 或 SCP 服务器)上的文件的访问。需要添加如下依赖
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
9. Temporary Files:提供对临时文件系统的访问,该文件系统在 Commons VFS 关闭时被删除。临时文件系统由本地文件系统支持
- tmp://[absolute-path] 示例:tmp://dir/somefile.txt
10. RAM:在内存中存储所有数据的文件系统(每个文件内容一个字节数组)
- ram://[path] 示例:ram:///any/path/to/file.txt
11. Resource :实际上不是一个文件系统,它使用 ClassLoader.getResource() 查找资源,并创建一个 VFS url 以供进一步处理
- res://[path]
12. CIFS
- smb://[username[:password]@]hostname[:port][absolute-path]
13. MIME:可以读取邮件及其附件,比如归档文件。如果已解析邮件中的某个部分没有名称,则将生成一个伪名称。虚拟名称是:_body_part_X,其中X将被零件号替换。
- [mime://mime-file-uri!absolute-path]
14. 自定义扩展
整体结构
FileSystemManager
管理一组文件系统。此接口用于按名称从这些文件系统之一中定位获取 org.apache.commons.vfs2.FileObject 。
FileProvider
文件提供者。每个文件提供者负责处理特定 URI 的文件。
FileNameParser
提供将文件名解析为 org.apache.commons.vfs2.FileName 的方法。
FileSystem
一个文件系统,由文件的层次结构组成。
FileObject
代表一个文件,用于访问文件的内容和结构。有两种类型的文件:文件夹,包含其他文件;普通文件,包含数据或内容。一个文件夹可能没有任何内容,普通文件不能包含其他文件。
FilesCache
文件缓存接口。VFS 内置一下几种实现,SoftRefFilesCache(软引用,默认值),WeakRefFilesCache(弱引用),DefaultFilesCache(没有过期和限制),LRUFilesCache(LRU实现,默认容量100个),NullFilesCache(空实现,不做任何缓存)。
FileSelector
该接口用于查找子文件时定义选择规则,使用方式 FileObject.findFiles(FileSelector) 。VFS 内置了 7 种选择器。
public static final FileSelector SELECT_SELF = new FileDepthSelector();
public static final FileSelector SELECT_SELF_AND_CHILDREN = new FileDepthSelector(0, 1);
public static final FileSelector SELECT_CHILDREN = new FileDepthSelector(1);
public static final FileSelector EXCLUDE_SELF = new FileDepthSelector(1, 2147483647);
public static final FileSelector SELECT_FILES;
public static final FileSelector SELECT_FOLDERS;
public static final FileSelector SELECT_ALL;
Apache VFS 提供四个FileSelector实现类:
AllFileSelector
顾名思义,将选择所有文件FileDepthSelector (int minDepth, int maxDepth)
选择特定深度的所有文件,以最小深度,最大深度为参数FileFilterSelector
选择所有给定文件对象的子文件。和FileFilter非常象。(那还要这个干什么??),可以接受一个FileFilter作为参数FileTypeSelector (FileType type)
选择特定类型的文件。 Apache VFS的文件类型FileType对象只有四种类型:- FILE 文件
- FILE_OR_FOLDER 文件或目录
- FOLDER 目录
- FILE_IMAGINARY 尚不存在的文件
加载流程
VFS 加载文件流程大致如下:FileSystemManager 解析文件名,通过文件名中的协议(如ftp://中的ftp)获取对应 FileProvider 对象,FileProvider 通过 FileNameParser 对象解析文件名获取对应的 FileSystem 对象,通过 FileSystem 对象的 resolveFile 方法获取文件(默认先从缓存中查找,不存在再调用 createFile 方法创建 FileObject 对象,FileObject 就是实体文件的抽象,提供读取和修改等相关能力)
使用apache-commons-vfs
- 引入依赖
<!-- other -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.31</version>
</dependency>
<!-- vfs -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-vfs2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!--访问Sftp服务器文件时需要引入-->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<!--访问Http服务器文件时需要引入-->
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
- 编写代码
package com.xiaoai.apache_vfs;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.vfs2.*;
import org.apache.commons.vfs2.provider.ftp.FtpFileSystemConfigBuilder;
import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Date;
/**
* @Author
* @Date 2021-10-24 14:33
*/
public class MyVfsTest {
private static final Logger logger = LoggerFactory.getLogger(MyVfsTest.class);
private static FileSystemManager fsMng = null;
/**
* 初始化vfs
* @return
* @throws FileSystemException
*/
public synchronized FileSystemManager vfsInit() throws FileSystemException {
logger.debug("vfs使用,初始化信息...");
if (fsMng == null) {
try {
FTPSetting();
SFTPSetting();
fsMng = VFS.getManager();
} catch (FileSystemException ex) {
logger.debug("vfs使用,初始化信息...失败",ex);
throw new FileSystemException(ex);
}
}
return fsMng;
}
private static FileSystemOptions FTPSetting(){
FtpFileSystemConfigBuilder builder = FtpFileSystemConfigBuilder.getInstance();
FileSystemOptions options = new FileSystemOptions();
//解决中文乱码
builder.setControlEncoding(options, "UTF-8");
builder.setServerLanguageCode(options, "zh");
return options;
}
private static FileSystemOptions SFTPSetting() throws FileSystemException {
FileSystemOptions options = new FileSystemOptions();
SftpFileSystemConfigBuilder sftpBuilder = SftpFileSystemConfigBuilder.getInstance();
// 设false时,URI要传绝对路径,设true时,URI传相对于远程用户根目录的相对路径
sftpBuilder.setUserDirIsRoot(options, true);
sftpBuilder.setStrictHostKeyChecking(options, "no");
// sftpBuilder.setTimeout(options, 10000);
return options;
}
public static void delete(String path) {
try {
FileObject fo = fsMng.resolveFile(path);
fo.delete();
} catch (FileSystemException e) {
e.printStackTrace();
}
}
public static boolean isDirectory(String path) {
try {
FileObject fo = fsMng.resolveFile(path);
return fo.getType().equals(FileType.FOLDER);
} catch (FileSystemException e) {
e.printStackTrace();
}
return false;
}
/**
* 获取输入流
*
* @param url
* @return 文件输入流
* @throws IOException
*/
public static InputStream getFileToInputStream(String url) throws IOException{
try {
FileObject fo = fsMng.resolveFile(url);
return fo.getContent().getInputStream();
} catch (FileSystemException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public static OutputStream getOutputStream(String path) {
try {
FileObject fo = fsMng.resolveFile(path);
return fo.getContent().getOutputStream();
} catch (FileSystemException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* 获取字节数组
*
* @param url
* @return 文件字节数组
* @throws IOException
*/
public static byte[] getFileToByte(String url) throws IOException{
InputStream inputStream = getFileToInputStream(url);
return IOUtils.toByteArray(inputStream);
}
/**
* 文件上传
* @param uploadURL
* @param bytes
* @return
*/
public static boolean uploadFileToByte(String uploadURL,byte[] bytes){
OutputStream output = null;
FileObject ftpFile = null;
try {
long byteLength = bytes.length;
ftpFile = fsMng.resolveFile(uploadURL);
if(ftpFile != null && ftpFile.exists() == false){
ftpFile.createFile();
}
output = ftpFile.getContent().getOutputStream();
IOUtils.write(bytes,output);
if(ftpFile.isFile()){
long size = ftpFile.getContent().getSize();
if(byteLength == size){
logger.debug("文件上传成功size:{}",size);
return true;
}
}else{
logger.debug("创建文件夹成功getURL:{}",ftpFile.getURL());
return true;
}
return false;
}catch (Exception e) {
e.printStackTrace();
}finally{
if(output != null){
try {
output.close();
output.flush();
ftpFile.close();
} catch (Exception var2) {
var2.printStackTrace();
}
}
}
return false;
}
public static boolean isFile(String path) {
try {
FileObject fo = fsMng.resolveFile(path);
return fo.getType().equals(FileType.FILE);
} catch (FileSystemException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
/**
* 函数描述:根据传入的文件路径创建文件夹(包括各级父文件夹)。如果路径中有文件,会自动去掉文件名。 (文件的判断是
* 以最后一个"/"之后是否有"."为标识的,)
*
* @param path
* @return 如果创建成功,返回true;否则,返回false;
*/
public static boolean mkdirs(String path) {
String realPath = "";
path = path.replaceAll("\\\\", "/");
// 如果该路径已"/"结尾,则整个字符串都是路径
if (path.endsWith("/")) {
realPath = path;
} else {
int fileNamePoint = path.lastIndexOf("/");
// 获取真正的路径
if (fileNamePoint >= 0) {
realPath = path.substring(0, fileNamePoint);
}
// 读取文件名
String fileName = path.substring(fileNamePoint + 1);
// 如果读取的文件名中没有".",说明整个字符串都是路径
if (fileName.indexOf(".") < 0) {
realPath = path;
}
}
try {
FileObject fo = fsMng.resolveFile(realPath);
fo.createFolder();
return true;
} catch (Exception e) {
return false;
}
}
/**
* 函数描述:对文件进行copy
*
* @param sourceFilePath 源文件的全部路径+文件名
* @param targetFilePath 目标文件的全部路径+文件名
* @param overWrite 如果目标文件存在,是否覆盖。true:覆盖;false:不覆盖(当源文件和目标文件都存在并且不覆盖时,返回true)。
* @return true:成功;false:失败; (当源文件和目标文件都存在并且不覆盖时,返回true)。
*/
public static boolean copyFile(String sourceFilePath, String targetFilePath, boolean overWrite) throws IOException {
if (StringUtils.isBlank(sourceFilePath) || StringUtils.isBlank(targetFilePath)) {
throw new IOException("源文件或者目标文件为空");
}
FileObject from = fsMng.resolveFile(sourceFilePath);
FileObject to = fsMng.resolveFile(targetFilePath);
if (to.exists()) {
if (to.getType() == FileType.FILE) {
if (overWrite && !to.delete()) {
throw new IOException("目标文件[" + targetFilePath + "]被保护,不能被覆盖!");
} else if (!overWrite) {
throw new IOException("目标文件[" + targetFilePath + "]已经存在!");
}
}
}
to.copyFrom(from, Selectors.SELECT_ALL);
return true;
}
/**
* Moving a File to Another File ,没有进行磁盘空间大小的判断
*
* @param srcFile 源文件 eg: c:\windows\abc.txt
* @param targetFile 目标文件 eg: c:\temp\abc.txt
* @param overWrite 如果目标文件存在,是否覆盖
* @return success
*/
public static boolean moveFile(String srcFile, String targetFile, boolean overWrite) throws IOException {
if (srcFile.equals(targetFile)) {
return true;
}
FileObject src = fsMng.resolveFile(srcFile);
// File (or directory) to be moved
if (StringUtils.isNotBlank(srcFile) && !src.exists()) {
throw new IOException("源文件[" + srcFile + "]不存在");
}
// Destination directory
FileObject to = fsMng.resolveFile(targetFile);
if (to.exists()) {
if (to.getType() == FileType.FILE) {
if (overWrite && !to.delete()) {
throw new IOException("目标文件[" + targetFile + "]被保护,不能被覆盖!");
} else if (!overWrite) {
throw new IOException("目标文件[" + targetFile + "]已经存在!");
}
}
}
src.moveTo(to);
return true;
}
public static void print(String path) {
print(path,null);
}
public static void print(String path, String printPath) {
try {
FileSystemManager fsManager = VFS.getManager();
FileSystemOptions opts = new FileSystemOptions();
if (path.startsWith("sftp:")) {
FTPSetting();
}
FileObject fileObject = fsManager.resolveFile(path, opts);
if (fileObject.isFolder()) {
FileObject[] childs = fileObject.getChildren();
for (FileObject child : childs) {
print(child,printPath);
}
} else {
print(fileObject,printPath);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void print(FileObject fileObject, String printPath) throws Exception {
if (printPath != null){
System.setOut(new PrintStream(printPath));
}
System.out.println("\n----------");
System.out.println("url:" + fileObject.getPublicURIString());
System.out.println("ModifiedTime:" + new Date(fileObject.getContent().getLastModifiedTime()));
System.out.println("---------- content ----------");
System.out.println(IOUtils.toString(fileObject.getContent().getInputStream(), "UTF-8"));
}
@Before
public void init() throws FileSystemException {
vfsInit();
}
@After
public void after(){
fsMng.close();
}
@Test
public void testResolve() throws IOException {
FileObject fo = fsMng.resolveFile("D:\\tem2\\test\\temp2.txt");
FileObject foFolder = fsMng.resolveFile("D:\\tem2\\test");
/**
* 获取属性
* 结果:
* org.apache.commons.vfs2.provider.local.LocalFileSystem@4f970963
* parent:file:///D:/tem2/test
* name:file:///D:/tem2/test/temp2.txt
* path:D:\tem2\test\temp2.txt
* pubURI:file:///D:/tem2/test/temp2.txt
* URI:file:///D:/tem2/test/temp2.txt
* URL:file:///D:/tem2/test/temp2.txt
* 是否文件:true
* 是否文件夹:false
* 是否符号链接:false
* 是否可执行:true
* 是否隐藏:false
* type:file
*/
// System.out.println("/n----------");
// System.out.println(fo.getFileSystem()); // LocalFileSystem
// System.out.println("parent:"+fo.getParent().toString());
// System.out.println("name:"+fo.getName());
// System.out.println("path:"+fo.getPath());
// System.out.println("pubURI:"+fo.getPublicURIString());
// System.out.println("URI:"+fo.getURI().toString());
// System.out.println("URL:"+fo.getURL());
//
// System.out.println("是否文件:" + fo.isFile());
// System.out.println("是否文件夹:" + fo.isFolder());
// System.out.println("是否符号链接:" + fo.isSymbolicLink());
// System.out.println("是否可执行:" + fo.isExecutable());
// System.out.println("是否隐藏:" + fo.isHidden());
// System.out.println("type:"+fo.getType());
/**
* 读取内容
*/
if (fo.isFile()) {
FileContent fc = fo.getContent();
// fc.getInputStream();
// fc.getByteArray();
// 获取内容 - 字符串形式
String content = fc.getString("UTF-8");
System.out.println(content);
}
/**
* 查找文件 获取子文件
*
* 结果:
* --file:///D:/tem2/test/temp2.txt
* --file:///D:/tem2/test/test1
* --file:///D:/tem2/test
*/
// System.out.println("/n----------");
// if (foFolder.isFolder()) {
// FileObject[] files = foFolder.findFiles(Selectors.SELECT_ALL);
// for (int i = 0; i < files.length; i++) {
// System.out.println("--" + files[i].getName());
// }
// // 获取所有子文件
// FileObject[] foArr = fo.getChildren();
// // 获取子文件(名称为test)
// FileObject test = fo.getChild("a.txt");
// // 从所有后代中获取类型是文件的文件
// FileObject[] files = fo.findFiles(Selectors.SELECT_FILES);
// }
/**
* 删除文件
*/
if (fo.isFolder()) {
// 删除此文件和所有子文件, 返回删除的数量
int delCount = fo.deleteAll();// 同fo.delete(Selectors.SELECT_ALL);
// 只删除所有子文件
int del2 = fo.delete(Selectors.EXCLUDE_SELF);
// 只删除直接子文件和空目录
int del3 = fo.delete(Selectors.SELECT_CHILDREN);
// 只删除文件
int del4 = fo.delete(Selectors.SELECT_FILES);
// 只删除空的子目录
int del5 = fo.delete(Selectors.SELECT_FOLDERS);
// 删除目录本身(如果包含子文件则删除失败返回0)
int del6 = fo.delete(Selectors.SELECT_SELF);
// 目录不为空则删除失败返回false
boolean suc = fo.delete();
} else if (fo.isFile()) {
// 删除文件本身
boolean suc = fo.delete();
}
// 关闭
fo.close();
}
@Test
public void testListener() throws IOException {
// 监听文件创建,修改或删除
FileSystemManager fsMgr = VFS.getManager();
String path = "D:\\tem2\\test\\a.txt";
FileObject fo = fsMgr.toFileObject(new File(path));
// 添加监听器
fo.getFileSystem().addListener(fo, new MyListener());
if (!fo.exists()) {
fo.createFile();
}
fo.setWritable(false, false);
// fo.delete();
fo.close();
}
private class MyListener implements FileListener {
@Override
public void fileCreated(FileChangeEvent event) throws Exception {
System.out.println("fileCreated:"+event.getFileObject().getName());
}
@Override
public void fileDeleted(FileChangeEvent event) throws Exception {
System.out.println("fileDeleted:"+event.getFileObject().getName());
}
@Override
public void fileChanged(FileChangeEvent event) throws Exception {
System.out.println("fileChanged:"+event.getFileObject().getName());
}
}
@Test
public void testDownForByte(){
try {
// 读取
System.out.println("----------");
// String url = "file:///D:/tem2/temp.txt";
// String url = "zip://file:///D:/tem2/晚安玫瑰.rar!/原始风景.txt";
String url = "sftp://root:root@192.168.43.128:22/temp/temp.txt";
byte[] bytes = getFileToByte(url);
System.out.println("读取:" + url);
System.out.println("读取到长度:" + bytes.length);
System.out.println("读取到内容:" + new String(bytes));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void test(){
try {
// 写入
System.out.println("----------");
String pathUrl = "sftp://root:root@192.168.43.128/temp/temp2.txt";
System.out.println("写入:" + pathUrl);
boolean result = uploadFileToByte(pathUrl, "hello temp2 > java:xiaoai".getBytes());
System.out.println("result:" + (result ? "writer success!" : "writer failed!"));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testMethod() throws IOException {
// copyFile("D:/tem2/java.jpeg","D:/tem2/javaCope.jpeg",true);
// moveFile("D:/tem2/javaCope.jpeg","D:/tem2/javaCopeMove.jpeg",true);
// print("D:\\tem2\\test");
// print("https://www.baidu.com/index.html");
print("https://www.baidu.com/index.html","D:\\tem2\\test\\temp2.txt");
}
}