ไฟล์เปลี่ยนฟังใน Java


104

ฉันต้องการรับการแจ้งเตือนเมื่อไฟล์มีการเปลี่ยนแปลงในระบบไฟล์ ฉันไม่พบอะไรเลยนอกจากเธรดที่สำรวจคุณสมบัติ lastModified File และเห็นได้ชัดว่าโซลูชันนี้ไม่เหมาะสม


ขอขอบคุณสำหรับการตอบสนองทุก. เนื่องจากความต้องการของฉันมีเพียงเล็กน้อย (ฉันต้องการเพียงโหลดไฟล์คุณสมบัติซ้ำในการเปลี่ยนแปลงในเว็บแอปพลิเคชันของฉัน .. ส่วนใหญ่เป็นเพราะการรีสตาร์ทเว็บแอปทั้งหมดนั้นค่อนข้างช้า) ฉันจะยังคงยึดติดกับแบบสำรวจ อย่างน้อยตอนนี้ฉันก็รู้แล้วว่าไม่มีวิธีง่ายๆสำหรับกรณีเหล่านี้
cheng81

14
เพียงแค่ทราบ เมื่อเราแก้ไขปัญหานี้ก่อนที่เราจะพบว่าไฟล์ขนาดใหญ่มักจะเข้ามาช้าและกลไกการสำรวจมักจะค้นพบไฟล์ใหม่หรือไฟล์ที่เปลี่ยนแปลงก่อนที่จะเขียนเสร็จสมบูรณ์ ในการแก้ปัญหานี้ได้มีการนำวิธีการแก้ปัญหาแบบ 'กัดสองครั้ง' ผู้สำรวจสังเกตเห็นว่าไฟล์มีการเปลี่ยนแปลง แต่ไม่ได้แจ้งให้ระบบของไฟล์ใหม่ / ที่เปลี่ยนแปลงจนกว่าจะมีลักษณะเหมือนกันสำหรับสองการสำรวจนั่นคือมีความเสถียร แก้ไขข้อผิดพลาด badfile จำนวนมาก
Steve Powell

1
นอกจากนี้ Tomcat ยังประสบปัญหานี้เมื่อทิ้งไฟล์ WAR ขนาดใหญ่ลงในโฟลเดอร์ webapps จากแหล่งข้อมูลระยะไกลและทำมานานแล้ว
John Rix

คำตอบ:


21

ในระดับต่ำวิธีเดียวในการสร้างโมเดลยูทิลิตี้นี้คือการมีเธรดการโพลในไดเร็กทอรีและคอยเฝ้าดูคุณลักษณะของไฟล์ แต่คุณสามารถใช้รูปแบบเพื่อพัฒนาอะแดปเตอร์สำหรับยูทิลิตี้ดังกล่าวได้

ตัวอย่างเช่นแอปพลิเคชันเซิร์ฟเวอร์ j2ee เช่น Tomcat และอื่น ๆ มีคุณสมบัติการโหลดอัตโนมัติโดยที่ในทันทีที่ตัวบอกการปรับใช้เปลี่ยนแปลงหรือคลาส servlet เปลี่ยนแอปพลิเคชันจะรีสตาร์ท

คุณสามารถใช้ไลบรารีจากเซิร์ฟเวอร์ดังกล่าวได้เนื่องจากโค้ดส่วนใหญ่ของ tomcat สามารถใช้ซ้ำได้และโอเพนซอร์ส


60
สิ่งนี้ไม่เป็นความจริงอีกต่อไปใน Java 7: ขณะนี้มี API สำหรับสิ่งนี้ที่สามารถเชื่อมต่อกับบริการแจ้งเตือนของระบบปฏิบัติการ: blogs.oracle.com/thejavatutorials/entry/…
Arnout Engelen

1
API นั้นไม่เพียงพออย่างมากไม่มีการแจ้งเตือนสำหรับเหตุการณ์การปิดไฟล์บน Linux ดังนั้นในกรณีง่ายๆเมื่อปิดไฟล์คัดลอกไปยังไดเร็กทอรีอื่นไม่ทำงาน
binarytemple_picsolve

API นั้นซับซ้อนเกินไป หากบังคับให้คุณใช้งาน / จัดการเธรดด้วยการวนซ้ำแบบ "ไม่มีที่สิ้นสุด" เพียงเพื่อใช้ จากนั้นคุณต้องระวังเหตุการณ์ที่เรียกว่า "OVERFLOW" ซึ่งหมายความว่าคุณพลาดอะไรไป แต่ใครจะรู้บ้างว่า จริง ๆ แล้วมันเพิ่มงานในโปรแกรมเมอร์เพื่อประโยชน์ที่มากเกินไปของการแจ้งเตือนการเปลี่ยนแปลง dir ดั้งเดิม (แม้ว่าระบบไฟล์จะได้รับการสนับสนุนหรือไม่ก็จะเป็นค่าเริ่มต้นสำหรับการสำรวจภายในอยู่ดี) คงจะดีไม่น้อยหากเป็นไปตามรูปแบบการฟังที่เรียบง่าย ตัวอย่างเช่น WatchService.watch (File f, FileListener listener) โดย f อาจเป็น dir หรือ file เลวร้ายเกินไป.
Chris Janicki

117

ฉันเคยเขียนการตรวจสอบไฟล์บันทึกมาก่อนและพบว่าผลกระทบต่อประสิทธิภาพของระบบในการสำรวจคุณลักษณะของไฟล์เดียวสองสามครั้งต่อวินาทีนั้นน้อยมาก

Java 7 ซึ่งเป็นส่วนหนึ่งของ NIO.2 ได้เพิ่มWatchService API

WatchService API ได้รับการออกแบบมาสำหรับแอปพลิเคชันที่ต้องได้รับการแจ้งเตือนเกี่ยวกับเหตุการณ์การเปลี่ยนแปลงไฟล์


11
ฉันเห็นตัวอย่างเป็นไดเร็กทอรีการดู แต่ไฟล์แต่ละไฟล์ล่ะ
Archimedes Trajano

@ArchimedesTrajano API จะแจ้งให้คุณทราบเมื่อไฟล์ในไดเร็กทอรีมีการเปลี่ยนแปลง เหตุการณ์ที่ทริกเกอร์รวมถึงชื่อของไฟล์ที่มีการเปลี่ยนแปลง ดังนั้นคุณสามารถจัดการกับเหตุการณ์สำหรับไฟล์บางไฟล์หรือบางไฟล์และไม่สนใจผู้อื่น
Michael


39

ฉันใช้ VFS API จาก Apache Commons นี่คือตัวอย่างวิธีการตรวจสอบไฟล์โดยไม่ส่งผลกระทบต่อประสิทธิภาพมากนัก:

DefaultFileMonitor


27

มี lib ที่เรียกว่าjnotifyที่ห่อinotifyบน linux และยังรองรับ windows ไม่เคยใช้และไม่รู้ว่ามันดีแค่ไหน แต่ก็น่าลองนะฉันจะบอกว่า


1
ทำงานได้อย่างสมบูรณ์แบบและใช้งานง่ายสุด ๆ ฉันใช้ inotify มาหลายปีแล้ว รวดเร็วเสถียรและเชื่อถือได้
oᴉɹǝɥɔ

8

คอมมอนส์-io Java มีFileAlterationObserver มันทำการสำรวจร่วมกับ FileAlterationMonitor คล้ายกับคอมมอนส์ VFS ข้อดีคือมีการพึ่งพาน้อยกว่ามาก

แก้ไข: การอ้างอิงน้อยไม่เป็นจริงซึ่งเป็นทางเลือกสำหรับ VFS แต่ใช้ไฟล์ java แทนเลเยอร์นามธรรม VFS


5

"คุณสมบัติอื่น ๆ ของ NIO" มีฟังก์ชันการเฝ้าดูไฟล์โดยการใช้งานจะขึ้นอยู่กับระบบปฏิบัติการพื้นฐาน ควรอยู่ใน JDK7

ปรับปรุง: ถูกบันทึกอยู่ใน Java SE 7. คริส Janicki ข้อเสนอการเชื่อมโยงไปเกี่ยวข้องกวดวิชา Java


คุณสามารถระบุเพิ่มเติมหรือโพสต์ลิงก์ไปยังเอกสารประกอบได้ไหม ไม่พบสิ่งใดในสิ่งนี้ ...
Luciano

java.nio.fileมันน่าจะเป็นแพคเกจ ดูกวดวิชา
David L.


4

ฉันเรียกใช้ส่วนย่อยของโค้ดนี้ทุกครั้งที่ฉันไปอ่านไฟล์คุณสมบัติโดยจะอ่านไฟล์นั้นหากมีการแก้ไขตั้งแต่ครั้งสุดท้ายที่ฉันอ่านเท่านั้น หวังว่านี่จะช่วยใครบางคนได้

private long timeStamp;
private File file;

private boolean isFileUpdated( File file ) {
  this.file = file;
  this.timeStamp = file.lastModified();

  if( this.timeStamp != timeStamp ) {
    this.timeStamp = timeStamp;
    //Yes, file is updated
    return true;
  }
  //No, file is not updated
  return false;
}

วิธีการที่คล้ายกันถูกนำมาใช้ใน FileWatchdogLog4J


2

คุณสามารถฟังการเปลี่ยนแปลงไฟล์โดยใช้ FileReader โปรดดูตัวอย่างด้านล่าง

// File content change listener 
private String fname;
private Object lck = new Object();
... 
public void run()
{
    try
    {
        BufferedReader br = new BufferedReader( new FileReader( fname ) );
        String s;
        StringBuilder buf = new StringBuilder();
        while( true )
        {
            s = br.readLine();
            if( s == null )
            {
                synchronized( lck )
                {
                    lck.wait( 500 );
                }
            }
            else
            {
               System.out.println( "s = " + s );
            }

        }
    }
    catch( Exception e )
    {
        e.printStackTrace();
    }
}

2

หากคุณยินดีที่จะมีส่วนร่วมด้วยเงิน JNIWrapper เป็นไลบรารีที่มีประโยชน์พร้อม Winpack คุณจะสามารถรับเหตุการณ์ระบบไฟล์ในไฟล์บางไฟล์ได้ น่าเสียดายที่ windows เท่านั้น

ดูhttps://www.teamdev.com/jniwrapper

มิฉะนั้นการใช้รหัสเนทีฟก็ไม่ใช่เรื่องเลวร้ายเสมอไปโดยเฉพาะอย่างยิ่งเมื่อข้อเสนอที่ดีที่สุดคือกลไกการสำรวจความคิดเห็นเช่นเดียวกับเหตุการณ์ที่เกิดขึ้นเอง

ฉันสังเกตเห็นว่าการทำงานของระบบไฟล์ Java อาจช้าในคอมพิวเตอร์บางเครื่องและอาจส่งผลต่อประสิทธิภาพของแอปพลิเคชันได้ง่ายหากไม่ได้รับการจัดการที่ดี


1

คุณอาจพิจารณา Apache Commons JCI (Java Compiler Interface) แม้ว่า API นี้ดูเหมือนจะเน้นไปที่การรวบรวมคลาสแบบไดนามิก แต่ก็มีคลาสใน API ที่ตรวจสอบการเปลี่ยนแปลงของไฟล์ด้วย

ตัวอย่าง: http://commons.apache.org/jci/usage.html


1

บูรณาการในฤดูใบไม้ผลิให้เป็นกลไกที่ดีสำหรับการดูไดเรกทอรีและไฟล์: http://static.springsource.org/spring-integration/reference/htmlsingle/#files ค่อนข้างแน่ใจว่ามันเป็นข้ามแพลตฟอร์ม (ฉันเคยใช้กับ mac, linux และ windows)


1

มีไลบรารีข้ามเดสก์ท็อปเชิงพาณิชย์สำหรับไฟล์และโฟลเดอร์ที่เรียกว่า JxFileWatcher สามารถดาวน์โหลดได้จากที่นี่: http://www.teamdev.com/jxfilewatcher/

นอกจากนี้คุณสามารถดูได้ทางออนไลน์: http://www.teamdev.com/jxfilewatcher/onlinedemo/


1

การสำรวจคุณสมบัติไฟล์ที่แก้ไขล่าสุดเป็นวิธีง่ายๆ แต่มีประสิทธิภาพ เพียงกำหนดคลาสที่ขยายของฉันFileChangedWatcherและใช้onModified()วิธีการ:

import java.io.File;

public abstract class FileChangedWatcher
{
    private File file;

    public FileChangedWatcher(String filePath)
    {
        file = new File(filePath);
    }

    public void watch() throws InterruptedException
    {
        long currentModifiedDate = file.lastModified();

        while (true)
        {
            long newModifiedDate = file.lastModified();

            if (newModifiedDate != currentModifiedDate)
            {
                currentModifiedDate = newModifiedDate;
                onModified();
            }

            Thread.sleep(100);
        }
    }

    public String getFilePath()
    {
        return file.getAbsolutePath();
    }

    protected abstract void onModified();
}

0

เช่นเดียวกับคำตอบอื่น ๆ นี่คือวิธีที่ฉันใช้ File, Timer และ TimerTask เพื่อให้สิ่งนี้ทำงานเป็นการสำรวจเธรดพื้นหลังตามช่วงเวลาที่กำหนด

import java.io.File;
import java.util.Timer;
import java.util.TimerTask;

public class FileModifiedWatcher
{
  private static File file;
  private static int pollingInterval;
  private static Timer fileWatcher;
  private static long lastReadTimeStamp = 0L;

  public static boolean init(String _file, int _pollingInterval)
  {
    file =  new File(_file);
    pollingInterval = _pollingInterval; // In seconds

    watchFile();

    return true;
  }

  private static void watchFile()
  {
    if ( null == fileWatcher )
    {
      System.out.println("START");

      fileWatcher = new Timer();

      fileWatcher.scheduleAtFixedRate(new TimerTask()
      {
        @Override
        public void run()
        {

          if ( file.lastModified() > lastReadTimeStamp )
          {
            System.out.println("File Modified");
          }

          lastReadTimeStamp = System.currentTimeMillis();
        }
      }, 0, 1000 * pollingInterval);
    }

  }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.