我正在使用Java 1.4.2和Debian 6.0.3。网络中有一个共享的Windows文件夹,它通过fstab正确安装到/mnt/share/
(例如,它可以从操作系统完全可见并允许所有操作)使用CIFS。但是,当我尝试在Java中执行此操作时:
System.out.println(new File("/mnt/share/").listFiles().length)
它始终会返回0
,这意味着File[]
返回的listFiles
为空。同样的问题适用于/mnt/share/
的每个子目录。 list
也返回空数组。有趣的是,其他File
函数如“create”,“isDirectory”甚至“delete”都可以正常工作。从USB闪存盘(fat32)安装的目录也可以正常工作。
我在不同Windows系统的2个不同的“共享文件夹”上测试了这个;一个使用基于域的身份验证系统,另一个使用“简单共享” - 即访客访问。这种情况看起来很奇怪,因为挂载的目录应该成为文件系统的一部分,所以任何程序都可以使用它。或者至少我想过。
我想在我的程序中删除一个目录,除了listFiles
上的递归遍历之外,我目前看不到其它方法,所以这个bug变得相当烦人。我能想到的唯一“解决方法”是以某种方式运行外部bash脚本,但它似乎是一个可怕的解决方案。
编辑:这似乎是特定于1.4.2的错误,在Java 6中一切正常。但是我无法迁移,所以问题仍然存在。
你能建议一些解决方法吗?最好不要切换到第三方库而不是本机库,我不能说我喜欢为单个代码行重写整个项目的想法。
答案 0 :(得分:0)
由于Java 1.2
有方法File.getCanonicalFile()
。对于已安装目录的情况,您应该使用这样的样式:
new File("/mnt/share/").getCanonicalFile().listFiles()
答案 1 :(得分:0)
所以,两年半后放弃我遇到同样的问题,再次坚持使用1.4.2,因为我需要将代码嵌入到过时的Oracle Forms 10g版本中。
如果有人偶然遇到这个问题并决定正确地解决它,而不是破解它,那很可能与CIFS在安装远程文件系统时所做的(高度)异常inode映射有关,导致更多隐藏的错误,其中一些可以在serverfault上找到。这种映射的副作用之一是所有目录都没有硬链接计数。另一个是所有目录都具有正好为0的“大小”,而不是通常的“扇区大小或更多”,即使使用ls
也可以检查。
我不能确定没有检查(专有)源代码,但我可以猜测1.5之前的Java使用了一些快捷方式,比如在内部检查链接计数而不是实际用C调用readdir(),这同样适用于任何已安装的FS。
无论如何,第二个副作用可用于在File周围创建一个简单的包装器,它不依赖于系统调用,除非它怀疑使用CIFS挂载目录。 list
中listFiles
和java.io.File
函数的其他版本,即使是使用过滤器的函数,也会在内部依赖list()
,因此可以仅覆盖它。
我不关心listFiles
返回File[]
而不是FileEx[]
所以我没有费心去覆盖它,但应该足够简单。显然,该代码只能在具有ls
命令的类Unix系统中使用。
package FSTest;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
public class FileEx extends File
{
public FileEx(String path)
{
super(path);
}
public FileEx(File f)
{
super(f.getAbsolutePath());
}
public String[] list()
{
if (this.canRead() && this.isDirectory())
{
/*
* Checking the length of dir is not the most reliable way to distinguish CIFS mounts.
* However, zero directory length generally indicates something unusual,
* so calling ls on it wouldn't hurt. Ordinary directories don't suffer any overhead this way.
* If this "zero-size" behavior is ever changed by CIFS but list() still won't work,
* it will be safer to call super.list() first and call this.listUsingExec if returned array has 0 elements.
* Though it might have serious performance implications, of course.
*/
if (this.length() > 0)
return super.list();
else
return this.listUsingExec();
}
else
return null;
}
private String[] listUsingExec()
{
Process p;
String command = "/bin/ls -1a " + this.getAbsolutePath();
ArrayList list = new ArrayList();
try
{
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
for (String line = reader.readLine(); line != null; line = reader.readLine())
{
if (!line.equalsIgnoreCase(".") && !line.equalsIgnoreCase(".."))
list.add(line);
}
String[] ret = new String[list.size()];
list.toArray(ret);
return ret;
}
catch (IOException e)
{
return null;
}
}
}