我正在对文件进行递归复制,就像xcopy /D
我只想复制较新的文件目标文件一样(我不能直接使用xcopy
,因为我需要在复制过程中更改一些文件)。
在java中,我lastModified()
用来检查目标文件是否比源文件旧并且速度很慢。
- 我可以加快这个过程(也许使用 JNI 吗??)?
- 是否有任何其他复制脚本可以更好地完成这项工作(复制新文件 + 正则表达式更改一些文本文件)?
无论如何复制文件都不是一种选择,因为这将花费比检查上次修改日期(通过网络复制)更多的时间。
我正在对文件进行递归复制,就像xcopy /D
我只想复制较新的文件目标文件一样(我不能直接使用xcopy
,因为我需要在复制过程中更改一些文件)。
在java中,我lastModified()
用来检查目标文件是否比源文件旧并且速度很慢。
无论如何复制文件都不是一种选择,因为这将花费比检查上次修改日期(通过网络复制)更多的时间。
你需要确定它为什么这么慢。
当您运行程序时,您的进程的 CPU 利用率是多少。如果它超过 50% 的用户,那么你应该能够优化你的程序,如果它低于 20%,那么你可以做的不多。
通常这种方法很慢,因为您正在检查的文件是在磁盘上而不是在内存中。如果是这种情况,您需要加快访问磁盘的速度,或者获得更快的驱动器。例如,SSD 在执行此操作时可以快 10-100 倍。
批量查询可能会有所帮助。您可以通过使用多个线程来检查 lastModified 日期来做到这一点。例如有一个固定大小的线程池并为每个文件添加一个任务。线程池的大小决定了一次轮询的文件数。
这允许操作系统重新排序请求以适应磁盘上的布局。注意:理论上这很好,但是您必须测试这是否会使您的操作系统/硬件上的事情变得更快,因为它可能会使事情变慢。;)
所以我在网络驱动器上遇到了这个问题。痛苦。我有一个目录,上面有 17000 多个文件。在本地驱动器上,检查最后修改日期的时间不到 2 秒。在联网驱动器上花了 58 秒!!!当然,我的应用程序是一个交互式应用程序,所以我有一些抱怨。
经过一番研究,我决定可以实现一些 JNI 代码来执行 Windows Kernel32 findfirstfile/findnextfile/findclose 以显着改进该过程,但后来我有了 32 位和 64 位版本等。呃。然后失去跨平台能力。
尽管我在这里做了一些讨厌的黑客攻击。我的应用程序主要在 Windows 上运行,但我不想限制它这样做,所以我做了以下操作。检查我是否在 Windows 上操作。如果是这样,请查看我是否使用本地硬盘。如果没有,那么我们将使用 hackish 方法。
我存储了不区分大小写的所有内容。对于可能有一个目录同时包含文件“ABC”和“abc”的其他操作系统来说,这可能不是一个好主意。如果您需要关心这一点,那么您可以通过创建一个 new File("ABC") 和 new File("abc") 然后使用 equals 方法来比较它们来决定。在 Windows 等不区分大小写的文件系统上,它将返回 true,但在 unix 系统上,它将返回 false。
尽管在网络驱动器上花费的时间从 58 秒到 1.6 秒可能有点骇人听闻,但我可以忍受这些骇客。
boolean useJaveDefaultMethod = true;
if(System.getProperty("os.name").startsWith("Windows"))
{
File f2 = f.getParentFile();
while(true)
{
if(f2.getParentFile() == null)
{
String s = FileSystemView.getFileSystemView().getSystemTypeDescription(f2);
if(FileSystemView.getFileSystemView().isDrive(f2) && "Local Disk".equalsIgnoreCase(s))
{
useJaveDefaultMethod = true;
}
else
{
useJaveDefaultMethod = false;
}
break;
}
f2 = f2.getParentFile();
}
}
if(!useJaveDefaultMethod)
{
try
{
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/C", "dir " + f.getParent());
pb.redirectErrorStream(true);
Process process = pb.start();
InputStreamReader isr = new InputStreamReader(process.getInputStream());
BufferedReader br = new BufferedReader(isr);
String line;
DateFormat df = new SimpleDateFormat("dd-MMM-yy hh:mm a");
while((line = br.readLine()) != null)
{
try
{
Date filedate = df.parse(line);
String filename = line.substring(38);
dirCache.put(filename.toLowerCase(), filedate.getTime());
}
catch(Exception ex)
{
}
}
process.waitFor();
Long filetime = dirCache.get(f.getName().toLowerCase());
if(filetime != null)
return filetime;
}
catch(Exception Exception)
{
}
}
// this is SO SLOW on a networked drive!
long lastModifiedDate = f.lastModified();
dirCache.put(f.getName().toLowerCase(), lastModifiedDate);
return lastModifiedDate;
不幸的是,Java 处理查找 lastModified 的方式很慢(基本上它会在您请求信息时查询每个文件的底层文件系统,在 listFiles 或类似文件上没有批量加载此数据)。
您可能会调用更高效的本机程序来批量执行此操作,但任何此类解决方案都将与您部署到的平台密切相关。
我想您是通过网络进行此操作的,否则副本将毫无意义。网络目录操作很慢,运气不好。您总是可以将文件复制到某个大小阈值以下,无论使总操作花费最少的时间。
我不同意 Kris 的观点:Java 的做法并没有什么低效的地方,而且无论如何它确实必须这样做,因为你想要最新的价值。