Java:监视目录以移动大文件
我一直在写一个程序,监视一个目录,当在其中创建文件时,它会更改名称并将它们移动到一个新目录。在我的第一个实现中,我使用了Java的Watch Service API,当我测试1kb文件时,它工作得很好。出现的问题是,实际上创建的文件大小在50-300mb之间。发生这种情况时,WatcherAPI会立即找到该文件,但无法移动它,因为它仍在编写中。我试着将watcher放入一个循环(在文件被移动之前会产生异常),但这似乎效率很低 由于这不起作用,我尝试使用计时器,每隔10秒检查一次文件夹,然后在可能的时候移动文件。这就是我最终选择的方法 问题:在没有进行异常检查或持续比较大小的情况下,是否仍有信号表明文件写入完成?我喜欢对每个文件只使用一次WatcherAPI,而不是不断使用计时器检查(并遇到异常) 非常感谢所有回复Java:监视目录以移动大文件,java,directory,watch,Java,Directory,Watch,我一直在写一个程序,监视一个目录,当在其中创建文件时,它会更改名称并将它们移动到一个新目录。在我的第一个实现中,我使用了Java的Watch Service API,当我测试1kb文件时,它工作得很好。出现的问题是,实际上创建的文件大小在50-300mb之间。发生这种情况时,WatcherAPI会立即找到该文件,但无法移动它,因为它仍在编写中。我试着将watcher放入一个循环(在文件被移动之前会产生异常),但这似乎效率很低 由于这不起作用,我尝试使用计时器,每隔10秒检查一次文件夹,然后在可能
nt写入另一个文件,作为原始文件已完成的指示。 I.g“fileorg.dat”正在增长,如果完成创建文件“fileorg.done”并检查 仅适用于“fileorg.done” 使用巧妙的命名约定,您应该不会遇到问题。两种解决方案: 第一个是以下方面的细微变化: 对不完整的文件使用唯一前缀。类似于
myhugefile.zip.inc
而不是myhugefile.zip
。上载/创建完成后重命名文件。从手表中排除.inc文件
第二种方法是使用同一驱动器上的不同文件夹创建/上载/写入文件,并在文件准备就绪后将其移动到关注的文件夹。如果它们在同一个驱动器上,移动应该是一个原子操作(我想,这取决于文件系统)
无论哪种方式,创建文件的客户端都必须做一些额外的工作。我推测java.io.File.canWrite()将在文件编写完成后告诉您。这是一个非常有趣的讨论,当然,这是一个简单的用例:等待一个新文件被创建,然后以某种方式对该文件做出反应。这里的竞争条件很有趣,因为这里的高级需求当然是获取一个事件,然后实际获取(至少)文件的读锁。对于大文件或只是大量文件创建,这可能需要整个工作线程池,这些线程周期性地尝试锁定新创建的文件,当它们成功时,实际执行工作。但正如我确信NT所意识到的,我们必须小心地做到这一点,以使其具有可扩展性,因为这最终是一种轮询方法,可扩展性和轮询并不是两个很好地结合在一起的词。我今天遇到了同样的问题。我认为在文件实际导入之前的一个小延迟并不是什么大问题,我仍然希望使用NIO2 API。我选择的解决方案是,在对某个文件执行任何操作之前,等待10秒钟,直到该文件未被修改 实施的重要部分如下。程序将等待,直到等待时间到期或发生新事件。每次修改文件时,都会重置过期时间。如果在等待时间到期之前删除文件,则会将其从列表中删除。我使用poll方法,超时时间为预期的expirationtime,即(lastmodified+waitTime)-currentTime
private final Map<Path, Long> expirationTimes = newHashMap();
private Long newFileWait = 10000L;
public void run() {
for(;;) {
//Retrieves and removes next watch key, waiting if none are present.
WatchKey k = watchService.take();
for(;;) {
long currentTime = new DateTime().getMillis();
if(k!=null)
handleWatchEvents(k);
handleExpiredWaitTimes(currentTime);
// If there are no files left stop polling and block on .take()
if(expirationTimes.isEmpty())
break;
long minExpiration = min(expirationTimes.values());
long timeout = minExpiration-currentTime;
logger.debug("timeout: "+timeout);
k = watchService.poll(timeout, TimeUnit.MILLISECONDS);
}
}
}
private void handleExpiredWaitTimes(Long currentTime) {
// Start import for files for which the expirationtime has passed
for(Entry<Path, Long> entry : expirationTimes.entrySet()) {
if(entry.getValue()<=currentTime) {
logger.debug("expired "+entry);
// do something with the file
expirationTimes.remove(entry.getKey());
}
}
}
private void handleWatchEvents(WatchKey k) {
List<WatchEvent<?>> events = k.pollEvents();
for (WatchEvent<?> event : events) {
handleWatchEvent(event, keys.get(k));
}
// reset watch key to allow the key to be reported again by the watch service
k.reset();
}
private void handleWatchEvent(WatchEvent<?> event, Path dir) throws IOException {
Kind<?> kind = event.kind();
WatchEvent<Path> ev = cast(event);
Path name = ev.context();
Path child = dir.resolve(name);
if (kind == ENTRY_MODIFY || kind == ENTRY_CREATE) {
// Update modified time
FileTime lastModified = Attributes.readBasicFileAttributes(child, NOFOLLOW_LINKS).lastModifiedTime();
expirationTimes.put(name, lastModified.toMillis()+newFileWait);
}
if (kind == ENTRY_DELETE) {
expirationTimes.remove(child);
}
}
private final Map expirationTimes=newHashMap();
私有长newFileWait=10000L;
公开募捐{
对于(;;){
//检索并删除下一个监视键,如果不存在则等待。
WatchKey k=watchService.take();
对于(;;){
long currentTime=new DateTime().getMillis();
如果(k!=null)
handleWatchEvents(k);
handleExpiredWaitTimes(当前时间);
//如果没有剩余的文件,停止轮询并阻止.take()
if(expirationTimes.isEmpty())
打破
long minexption=min(expirationTimes.values());
长超时=最小消耗电流时间;
调试(“超时:”+超时);
k=watchService.poll(超时,时间单位为毫秒);
}
}
}
私有void handleExpiredWaitTimes(长currentTime){
//为过期时间已过的文件启动导入
for(条目:expirationTimes.entrySet()){
if(entry.getValue()kind=event.kind();
WatchEvent ev=铸造(事件);
路径名=ev.context();
路径子项=目录解析(名称);
如果(种类==条目|修改|种类==条目|创建){
//更新修改时间
FileTime lastModified=Attributes.readBasicFileAttributes(子级,无后续链接)。lastModifiedTime();
expirationTimes.put(名称,lastModified.toMillis()+newFileWait);
}
如果(种类==条目\删除){
过期时间。删除(子项);
}
}
当我实现了一个文件系统监视程序来传输上传的文件时,我不得不处理类似的情况。我为解决此问题而实现的解决方案包括以下内容:
1-首先,维护未处理文件的映射(只要文件仍在复制,文件系统将生成Modify_事件,因此如果标志为false,您可以忽略它们)
2-在fileProcessor中,您从列表中选取一个文件并检查它是否被文件系统锁定,如果是,您将得到一个异常,只需捕获该异常并将线程置于等待状态(即10秒)然后重试,直到释放锁。处理文件后,您可以将该标志更改为true或将其从映射中删除
如果在等待时间段内传输同一文件的多个版本,则此解决方案将无效
干杯,
Ramzi虽然它不是
boolean locked = true;
while (locked) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(file, "r"); // it will throw FileNotFoundException. It's not needed to use 'rw' because if the file is delete while copying, 'w' option will create an empty file.
raf.seek(file.length()); // just to make sure everything was copied, goes to the last byte
locked = false;
} catch (IOException e) {
locked = file.exists();
if (locked) {
System.out.println("File locked: '" + file.getAbsolutePath() + "'");
Thread.sleep(1000); // waits some time
} else {
System.out.println("File was deleted while copying: '" + file.getAbsolutePath() + "'");
}
} finally {
if (raf!=null) {
raf.close();
}
}
}
if (kind == ENTRY_CREATE) {
System.out.println("Creating file: " + child);
boolean isGrowing = false;
Long initialWeight = new Long(0);
Long finalWeight = new Long(0);
do {
initialWeight = child.toFile().length();
Thread.sleep(1000);
finalWeight = child.toFile().length();
isGrowing = initialWeight < finalWeight;
} while(isGrowing);
System.out.println("Finished creating file!");
}
public boolean acquireExclusiveReadLock( ... ) throws Exception {
LOG.trace("Waiting for exclusive read lock to file: {}", file);
// the trick is to try to rename the file, if we can rename then we have exclusive read
// since its a Generic file we cannot use java.nio to get a RW lock
String newName = file.getFileName() + ".camelExclusiveReadLock";
// make a copy as result and change its file name
GenericFile<T> newFile = file.copyFrom(file);
newFile.changeFileName(newName);
StopWatch watch = new StopWatch();
boolean exclusive = false;
while (!exclusive) {
// timeout check
if (timeout > 0) {
long delta = watch.taken();
if (delta > timeout) {
CamelLogger.log(LOG, readLockLoggingLevel,
"Cannot acquire read lock within " + timeout + " millis. Will skip the file: " + file);
// we could not get the lock within the timeout period, so return false
return false;
}
}
exclusive = operations.renameFile(file.getAbsoluteFilePath(), newFile.getAbsoluteFilePath());
if (exclusive) {
LOG.trace("Acquired exclusive read lock to file: {}", file);
// rename it back so we can read it
operations.renameFile(newFile.getAbsoluteFilePath(), file.getAbsoluteFilePath());
} else {
boolean interrupted = sleep();
if (interrupted) {
// we were interrupted while sleeping, we are likely being shutdown so return false
return false;
}
}
}
return true;
}
public void run()
{
try
{
WatchKey key = myWatcher.take();
while (key != null)
{
for (WatchEvent event : key.pollEvents())
{
if (FilenameUtils.isExtension(event.context().toString(), "filepart"))
{
System.out.println("Inside the PartFile " + event.context().toString());
} else
{
System.out.println("Full file Copied " + event.context().toString());
//Do what ever you want to do with this files.
}
}
key.reset();
key = myWatcher.take();
}
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
[info] application - /var/www/webdav/.davfs.tmp39dee1 was created
[info] application - /var/www/webdav/document.docx was created
[info] application - /var/www/webdav/.davfs.tmp054fe9 was created
[info] application - /var/www/webdav/document.docx was created
[info] application - /var/www/webdav/.DAV/__db.document.docx was created
val triggerFileName: String = triggerFileTempDir + orifinalFileName + "_" + Files.getLastModifiedTime(Paths.get(event.getFile.getName.getPath)).toMillis + "_0"
// creates trigger file in temporary directory
val triggerFile: File = new File(triggerFileName)
val isCreated: Boolean = triggerFile.createNewFile()
if (isCreated)
println("Trigger created: " + triggerFileName)
else
println("Error in creating trigger file: " + triggerFileName)
val actualPath : String = "Original file directory here"
val tempPath : String = "Trigger file directory here"
val folder : File = new File(tempPath)
val listOfFiles = folder.listFiles()
for (i <- listOfFiles)
{
// ActualFileName_LastModifiedTime_NumberOfTries
val triggerFileName: String = i.getName
val triggerFilePath: String = i.toString
// extracting file info from trigger file name
val fileInfo: Array[String] = triggerFileName.split("_", 3)
// 0 -> Original file name, 1 -> last modified time, 2 -> number of tries
val actualFileName: String = fileInfo(0)
val actualFilePath: String = actualPath + actualFileName
val modifiedTime: Long = fileInfo(1).toLong
val numberOfTries: Int = fileStats(2).toInt
val currentModifiedTime: Long = Files.getLastModifiedTime(Paths.get(actualFilePath)).toMillis
val differenceInModifiedTimes: Long = currentModifiedTime - modifiedTime
// checks if file has been copied completely(4 intervals of 5 mins each with no modification)
if (differenceInModifiedTimes == 0 && numberOfTries == 3)
{
FileUtils.deleteQuietly(new File(triggerFilePath))
println("Trigger file deleted. Original file completed : " + actualFilePath)
}
else
{
var newTriggerFileName: String = null
if (differenceInModifiedTimes == 0)
{
// updates numberOfTries by 1
newTriggerFileName = actualFileName + "_" + modifiedTime + "_" + (numberOfTries + 1)
}
else
{
// updates modified timestamp and resets numberOfTries to 0
newTriggerFileName = actualFileName + "_" + currentModifiedTime + "_" + 0
}
// renames trigger file
new File(triggerFilePath).renameTo(new File(tempPath + newTriggerFileName))
println("Trigger file renamed: " + triggerFileName + " -> " + newTriggerFileName)
}
}