Я пишу программу, которая использует FileSystemWatcher
для мониторинга изменений в заданном каталоге, а когда он получает событие OnCreated или OnChanged, он копирует созданные или измененные файлы в указанные каталоги. Сначала у меня были проблемы с тем, что события OnChanged/OnCreated могут быть отправлены дважды (неприемлемо в случае необходимости обработки файла 500 МБ), но я сделал это по этому пути и с тем, что я ДЕЙСТВИТЕЛЬНО БЛОКИРОВАН, получает следующее IOException
: Процесс не может получить доступ к файлу 'C: \ Where are Photos \ bookmarks (11) .html', потому что он используется другим процессом.C# Невозможно открыть файл для чтения
Таким образом, чтобы программа не копировала все файлы, она должна. Как я уже упоминал, когда пользователь использует эту программу, он задает контролируемый каталог, когда пользователь копирует/создает/изменяет файл в этом каталоге, программа должна получить событие OnCreated/OnChanged, а затем скопировать этот файл в несколько других каталогов. Ошибка во всех случаях, если пользователь копирует несколько файлов, которые должны перезаписывать другие в контролируемой папке или при копировании большого количества нескольких файлов или даже при копировании одного файла в контролируемый каталог. Вся программа довольно большая, поэтому я отправляю наиболее важные части. OnCreated:
private void OnCreated(object source, FileSystemEventArgs e) {
AddLogEntry(e.FullPath, "created", "");
// Update last access data if it's file so the same file doesn't
// get processed twice because of sending another event.
if (fileType(e.FullPath) == 2) {
lastPath = e.FullPath;
lastTime = DateTime.Now;
}
// serves no purpose now, it will be remove soon
string fileName = GetFileName(e.FullPath);
// copies file from source to few other directories
Copy(e.FullPath, fileName);
Console.WriteLine("OnCreated: " + e.FullPath);
}
OnChanged:
private void OnChanged(object source, FileSystemEventArgs e) {
// is it directory
if (fileType(e.FullPath) == 1)
return; // don't mind directory changes itself
// Only if enough time has passed or if it's some other file
// because two events can be generated
int timeDiff = ((TimeSpan)(DateTime.Now - lastTime)).Seconds;
if ((timeDiff < minSecsDiff) && (e.FullPath.Equals(lastPath))) {
Console.WriteLine("-- skipped -- {0}, timediff: {1}", e.FullPath, timeDiff);
return;
}
// Update last access data for above to work
lastPath = e.FullPath;
lastTime = DateTime.Now;
// Only if size is changed, the rest will handle other handlers
if (e.ChangeType == WatcherChangeTypes.Changed) {
AddLogEntry(e.FullPath, "changed", "");
string fileName = GetFileName(e.FullPath);
Copy(e.FullPath, fileName);
Console.WriteLine("OnChanged: " + e.FullPath);
}
}
FILETYPE:
private int fileType(string path) {
if (Directory.Exists(path))
return 1; // directory
else if (File.Exists(path))
return 2; // file
else
return 0;
}
Копия:
private void Copy(string srcPath, string fileName) {
foreach (string dstDirectoy in paths) {
string eventType = "copied";
string error = "noerror";
string path = "";
string dirPortion = "";
// in case directory needs to be made
if (srcPath.Length > fsw.Path.Length) {
path = srcPath.Substring(fsw.Path.Length,
srcPath.Length - fsw.Path.Length);
int pos = path.LastIndexOf('\\');
if (pos != -1)
dirPortion = path.Substring(0, pos);
}
if (fileType(srcPath) == 1) {
try {
Directory.CreateDirectory(dstDirectoy + path);
//Directory.CreateDirectory(dstDirectoy + fileName);
eventType = "created";
} catch (IOException e) {
eventType = "error";
error = e.Message;
}
} else {
try {
if (!overwriteFile && File.Exists(dstDirectoy + path))
continue;
// create new dir anyway even if it exists just to be sure
Directory.CreateDirectory(dstDirectoy + dirPortion);
// copy file from where event occured to all specified directories
using (FileStream fsin = new FileStream(srcPath, FileMode.Open, FileAccess.Read, FileShare.Read)) {
using (FileStream fsout = new FileStream(dstDirectoy + path, FileMode.Create, FileAccess.Write)) {
byte[] buffer = new byte[32768];
int bytesRead = -1;
while ((bytesRead = fsin.Read(buffer, 0, buffer.Length)) > 0)
fsout.Write(buffer, 0, bytesRead);
}
}
} catch (Exception e) {
if ((e is IOException) && (overwriteFile == false)) {
eventType = "skipped";
} else {
eventType = "error";
error = e.Message;
// attempt to find and kill the process locking the file.
// failed, miserably
System.Diagnostics.Process tool = new System.Diagnostics.Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = "\"" + srcPath + "\"";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = @"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern)) {
System.Diagnostics.Process.GetProcessById(int.Parse(match.Value)).Kill();
}
Console.WriteLine("ERROR: {0}: [ {1} ]", e.Message, srcPath);
}
}
}
AddLogEntry(dstDirectoy + path, eventType, error);
}
}
Я проверил везде в моей программе и всякий раз, когда я использую некоторые файл I используйте его в using
блоке, чтобы даже записывать событие в журнал (класс для того, что я пропустил, поскольку, вероятно, слишком много кода уже в сообщении) не будет блокировать файл, то есть он не должен, так как все операции используют using
блок оператора.
Я просто понятия не имею, кто запирает файл, если не моя программа «копирует» процесс через пользователя через Windows или что-то еще.
Прямо сейчас у меня есть два возможных «решения» (я не могу сказать, что они чистые решения, поскольку они хаки и как таковые не желательны). Так, вероятно, проблема с fileType
методом (что еще может заблокировать файл?) Я попытался изменить его к этому, чтобы имитировать «блокирование-до готовности к открытой» операции:
FILETYPE:
private int fileType(string path) {
FileStream fs = null;
int ret = 0;
bool run = true;
if (Directory.Exists(path))
ret = 1;
else {
while (run) {
try {
fs = new FileStream(path, FileMode.Open);
ret = 2;
run = false;
} catch (IOException) {
} finally {
if (fs != null) {
fs.Close();
fs.Dispose();
}
}
}
}
return ret;
}
Это работает так, как я мог сказать (тест), но ... это взломать, не говоря уже о других недостатках.
Другое «решение» Я мог бы попробовать (я еще не тестировал его) использует GC.Collect()
где-то в конце fileType()
метод. Может быть, даже хуже «решение», чем предыдущее.
Может ли кто-нибудь просить меня сказать, что на самом деле запирает файл, препятствуя его открытию и как я могу это исправить? Что мне не хватает, чтобы увидеть?
Заранее спасибо.
Большое спасибо за быстрый ответ, но мне не нужно разрешение на запись, мне нужно разрешение _read_ поэтому я могу прочитать его и скопировать в другие пункты назначения. – Maks
@Maks: Когда вы можете написать файл, вы также можете его прочитать. Если вы никогда не сможете писать, например. потому что папка защищена, вы также можете просто попытаться открыть файл только для чтения. –
Что я имел в виду, так это то, что, возможно, «легче» (более вероятно, чем разрешение на запись) получить разрешение на чтение, если это Windows-процесс (копия), чем разрешение на запись (поскольку он не завершился во время события) :) – Maks