Чтение изменений в файле в режиме реального времени с использованием.NET
У меня есть CSV-файл, который часто обновляется (около 20-30 раз в минуту). Я хочу вставить вновь добавленные строки в базу данных, как только они будут записаны в файл.
Класс FileSystemWatcher прослушивает уведомления об изменениях файловой системы и может вызывать событие всякий раз, когда происходит изменение в указанном файле. Проблема в том, что FileSystemWatcher не может точно определить, какие строки были добавлены или удалены (насколько я знаю).
Один из способов прочитать эти строки - сохранить и сравнить количество строк между изменениями и прочитать разницу между последним и вторым последним изменением. Тем не менее, я ищу более чистое (возможно, более элегантное) решение.
6 ответов
Я написал что-то очень похожее. Я использовал FileSystemWatcher для получения уведомлений об изменениях. Затем я использовал FileStream для чтения данных (отслеживая мою последнюю позицию в файле и просматривая ее перед чтением новых данных). Затем я добавляю прочитанные данные в буфер, который автоматически извлекает полные строки, а затем выводит их в пользовательский интерфейс.
Примечание: "this.MoreData(..) - это событие, слушатель которого добавляет в вышеупомянутый буфер и обрабатывает извлечение всей строки.
Примечание. Как уже упоминалось, это будет работать только в том случае, если изменения всегда являются дополнениями к файлу. Любые удаления вызовут проблемы.
Надеюсь это поможет.
public void File_Changed( object source, FileSystemEventArgs e )
{
lock ( this )
{
if ( !this.bPaused )
{
bool bMoreData = false;
// Read from current seek position to end of file
byte[] bytesRead = new byte[this.iMaxBytes];
FileStream fs = new FileStream( this.strFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite );
if ( 0 == this.iPreviousSeekPos )
{
if ( this.bReadFromStart )
{
if ( null != this.BeginReadStart )
{
this.BeginReadStart( null, null );
}
this.bReadingFromStart = true;
}
else
{
if ( fs.Length > this.iMaxBytes )
{
this.iPreviousSeekPos = fs.Length - this.iMaxBytes;
}
}
}
this.iPreviousSeekPos = (int)fs.Seek( this.iPreviousSeekPos, SeekOrigin.Begin );
int iNumBytes = fs.Read( bytesRead, 0, this.iMaxBytes );
this.iPreviousSeekPos += iNumBytes;
// If we haven't read all the data, then raise another event
if ( this.iPreviousSeekPos < fs.Length )
{
bMoreData = true;
}
fs.Close();
string strData = this.encoding.GetString( bytesRead );
this.MoreData( this, strData );
if ( bMoreData )
{
File_Changed( null, null );
}
else
{
if ( this.bReadingFromStart )
{
this.bReadingFromStart = false;
if ( null != this.EndReadStart )
{
this.EndReadStart( null, null );
}
}
}
}
}
Я думаю, что вы должны использовать NTFS Change Journal или подобное:
Журнал изменений используется NTFS для предоставления постоянного журнала всех изменений, внесенных в файлы на томе. Для каждого тома NTFS использует журнал изменений для отслеживания информации о добавленных, удаленных и измененных файлах. Журнал изменений гораздо более эффективен, чем отметки времени или файловые уведомления для определения изменений в данном пространстве имен.
Вы можете найти описание на TechNet. Вам нужно будет использовать PInvoke в.NET.
Правильно, FileSystemWatcher ничего не знает о содержимом вашего файла. Он скажет вам, если он изменился и т. Д., Но не то, что изменилось.
Вы только добавляете в файл? Из поста было немного неясно, были ли добавлены строки или их также можно удалить. Предполагая, что они добавлены, решение довольно простое, в противном случае вы будете делать некоторые сравнения.
Я бы сохранил текущий текст в памяти, если он достаточно мал, а затем использовал бы алгоритм сравнения, чтобы проверить, изменился ли новый текст и предыдущий текст. Эта библиотека, http://www.mathertel.de/Diff/, не только расскажет вам, что что-то изменилось, но и что изменилось. Таким образом, вы можете вставить измененные данные в БД.
Вы правы насчет FileSystemWatcher. Вы можете прослушивать созданные, измененные, удаленные и т. Д. События, но вы не получите глубже, чем файл, который их вызвал.
У вас есть контроль над самим файлом? Вы можете немного изменить модель, чтобы использовать файл как буфер. Вместо одного файла есть два. Один - это этап, один - сумма всей обработанной продукции. Прочитайте все строки из вашего "буферного" файла, обработайте их, затем вставьте их в конец другого файла, который является суммой всех обработанных строк. Затем удалите строки, которые вы обработали. Таким образом, вся информация в вашем файле ожидает обработки. Загвоздка в том, что если в системе есть что-то кроме записи (то есть также удаляет строки), то это не будет работать.
От всей моей головы, вы можете хранить последний известный размер файла. Сверьтесь с размером файла, и когда он изменится, откройте ридер.
Затем найдите читателя до вашего последнего размера файла и начните чтение оттуда.