/// <summary> /// /// </summary> /// <param name="item"></param> /// <param name="workspace"></param> public void file_create_acl(FileUpdateQueueItem item, IWorkspace workspace) { // 1. 対象ファイルが存在するかチェック if (!item.Target.Exists) { throw new ApplicationException("対象ファイルが指定位置に存在しません。"); } // 3. ACLファイルから、ACLハッシュを取得する var aclbin = VfsLogicUtils.ReadACLFile(new FileInfo(item.Target.FullName)); var aclhash = aclbin.FindKeyValue("ACLHASH"); // 4. データベースを参照し、ACLハッシュとファイルマッピング情報(AclHash)を突き合わせる IFileMappingInfo entity = mFileMappingInfoRepository.LoadByAclHash(aclhash); if (entity == null) { throw new ApplicationException(); } if (entity.GetWorkspace() == null || entity.GetWorkspace().Id != workspace.Id) { throw new ApplicationException(); } // 5. ファイルマッピング情報を参照し、物理ファイルが存在するかチェックする var phyFileInfo = new FileInfo(Path.Combine(workspace.PhysicalPath, entity.MappingFilePath)); if (!phyFileInfo.Exists) { throw new ApplicationException(); } // 6. 物理ファイルを、ACLファイルのパスに対応する物理空間のパスへ移動する // 移動先が、同じ場所となる場合は処理しない。 var aclfileLocalPath_Update = workspace.TrimWorekspacePath(item.Target.FullName); var extFilePath = Path.Combine(Path.GetDirectoryName(aclfileLocalPath_Update), Path.GetFileNameWithoutExtension(aclfileLocalPath_Update)); var toFileInfo = new FileInfo(Path.Combine(workspace.PhysicalPath, extFilePath)); if (phyFileInfo.FullName != toFileInfo.FullName) { Directory.CreateDirectory(toFileInfo.Directory.FullName); File.Move(phyFileInfo.FullName, toFileInfo.FullName); // 7. ファイルマッピング情報をDBに書き込む(コンテキスト初期化) entity = mFileMappingInfoRepository.LoadByAclHash(aclhash); entity.MappingFilePath = extFilePath; // 新しいファイルマップパス } mFileMappingInfoRepository.Save(); }
/// <summary> /// LLD-03-05-01:01-02-01 /// </summary> /// <param name="item"></param> /// <param name="workspace"></param> public void file_remove_acl(FileUpdateQueueItem item, IWorkspace workspace) { var aclfileLocalPath_Remove = workspace.TrimWorekspacePath(item.Target.FullName); var vrPath_Remove = Path.Combine(Path.GetDirectoryName(aclfileLocalPath_Remove), Path.GetFileNameWithoutExtension(aclfileLocalPath_Remove)); // 1. 削除したファイルパスと一致するファイルマッピング情報を取得する var fmi = mFileMappingInfoRepository.LoadByPath(vrPath_Remove); // 2. ファイルマッピング情報から、物理空間のファイルを特定する var phyFilePath = Path.Combine(workspace.PhysicalPath, vrPath_Remove); // 3. 物理空間のファイルを削除する File.Delete(phyFilePath); // 4. ファイルマッピング情報をデータベースから削除する mFileMappingInfoRepository.Delete(fmi); mFileMappingInfoRepository.Save(); }
/// <summary> /// ファイル更新情報キューに、ファイル更新情報を追加する /// </summary> /// <remarks> /// 対象パスがキューに登録済みの場合は、登録情報の更新を行います。 /// 未登録の場合は、情報を新規登録します。 /// </remarks> /// <param name="watchTarget">変更通知が発生した、ファイル情報</param> /// <param name="watcherChangeType">変更内容区分</param> /// <param name="beforeRenamedName">変更内容区分がリネームの場合、リネーム前のファイル名を入力してください</param> FileUpdateQueueItem UpdateOrInsertUpdatedFileQueueItem(FileInfo watchTarget, WatcherChangeTypes watcherChangeType, string beforeRenamedName) { FileUpdateQueueItem fileUpdateQueueItem; string key; lock (this) { // 更新イベントの対象ファイルが、ACLファイルか物理ファイルか更新キューに使用するキーが異なる。 // ACLファイルでは、ACLハッシュをキーに使用します。 // 物理ファイルでは、ファイルパスをキーに使用します。 if (watchTarget.Extension == ".aclgene") { // ACLファイルの場合、更新イベントを追わなくてもACLハッシュで常にどのファイルが更新キュー内のどこにあるかがわかる // ※ただし、ファイル削除を除く if (watcherChangeType == WatcherChangeTypes.Deleted) { var deletedFileRelativePath = this.mWorkspace.TrimWorekspacePath(watchTarget.FullName); var r = from u in mUpdatesWatchFiles where u.Value.OldRenameNamePath == deletedFileRelativePath select u; var prop = r.FirstOrDefault(); if (prop.Key != null) { mUpdatesWatchFiles.TryGetValue(prop.Key, out fileUpdateQueueItem); } else { fileUpdateQueueItem = new FileUpdateQueueItem { Target = watchTarget }; mUpdatesWatchFiles.AddOrUpdate(deletedFileRelativePath, fileUpdateQueueItem, (_key, _value) => fileUpdateQueueItem ); // 発生はありえないが、発生した場合は処理せず終了 // →発生はありうる。 //LOG.Warn("更新キューに登録されていないACLファイルの削除イベント"); //return null; } } else { // ACLファイルからACLデータを取得 AclFileStructure aclFileData; using (var file = File.OpenRead(watchTarget.FullName)) { aclFileData = Serializer.Deserialize <AclFileStructure> (file); } var aclhash = aclFileData.FindKeyValue("ACLHASH"); if (!mUpdatesWatchFiles.ContainsKey(aclhash)) { fileUpdateQueueItem = new FileUpdateQueueItem { Target = watchTarget }; mUpdatesWatchFiles.AddOrUpdate(aclhash, fileUpdateQueueItem, (_key, _value) => fileUpdateQueueItem); } else { // キューから情報を取得 mUpdatesWatchFiles.TryGetValue(aclhash, out fileUpdateQueueItem); fileUpdateQueueItem.Target = watchTarget; // 登録している物理ファイル情報を最新のオブジェクトにする } // 最後に更新イベントが発生した時のファイルパスを格納しておく(Deleteイベント用) fileUpdateQueueItem.OldRenameNamePath = this.mWorkspace.TrimWorekspacePath(watchTarget.FullName); } } else { if (watcherChangeType == WatcherChangeTypes.Renamed && !string.IsNullOrEmpty(beforeRenamedName)) { // 変更内容がリネームの場合、名前変更前のファイルパスで登録済みの項目を取得し、 // 名前変更前の項目はキューから削除します。 // 名前変更後の項目として、新たにキューに再登録を行います。 var renamedFullName = watchTarget.FullName.Replace(watchTarget.FullName, beforeRenamedName); var oldkey = this.mWorkspace.TrimWorekspacePath(renamedFullName); key = this.mWorkspace.TrimWorekspacePath(watchTarget.FullName); if (mUpdatesWatchFiles.ContainsKey(oldkey)) { // 古いキーの項目をキューから削除します。 // 新しいキーで、キューに情報を再登録します。 mUpdatesWatchFiles.TryRemove(oldkey, out fileUpdateQueueItem); mUpdatesWatchFiles.AddOrUpdate(key, fileUpdateQueueItem, (_key, _value) => fileUpdateQueueItem); } } else if (watcherChangeType == WatcherChangeTypes.Created) { key = this.mWorkspace.TrimWorekspacePath(watchTarget.FullName); } else { key = this.mWorkspace.TrimWorekspacePath(watchTarget.FullName); } // 更新通知があったファイルが処理キューに未登録の場合、キューに更新通知情報を新規登録します if (!mUpdatesWatchFiles.ContainsKey(key)) { fileUpdateQueueItem = new FileUpdateQueueItem { Target = watchTarget }; mUpdatesWatchFiles.AddOrUpdate(key, fileUpdateQueueItem, (_key, _value) => fileUpdateQueueItem); } else { // キューから情報を取得 mUpdatesWatchFiles.TryGetValue(key, out fileUpdateQueueItem); fileUpdateQueueItem.Target = watchTarget; // 登録している物理ファイル情報を最新のオブジェクトにする } // 更新通知のイベント区分が『リネーム』の場合、元のファイル名も保存しておく。 // イベント処理前は物理ディレクトリ空間のファイルパスはリネーム前のパスのままなので、 // リネーム前のファイル名のみを保存します。 if (watcherChangeType == WatcherChangeTypes.Renamed && string.IsNullOrEmpty(fileUpdateQueueItem.OldRenameNamePath)) { fileUpdateQueueItem.OldRenameNamePath = beforeRenamedName; } } // 情報に、履歴を追加 var now = DateTime.Now; fileUpdateQueueItem.LastUpdate = now; var rec = new RecentInfo { EventType = watcherChangeType, RecentDate = now }; fileUpdateQueueItem.Recents.Add(rec); return(fileUpdateQueueItem); } }
/// <summary> /// ファイルに対するVFS処理を定期実行します /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void OnIndexTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) { if (this.IsSuspendIndex) { return; // サスペンド時はインデックス生成処理はスキップする } // インデックス生成処理中は、このメソッドを呼び出すタイマーは停止しておきます。 var timer = sender as System.Timers.Timer; timer.Enabled = false; mLogger.Info("タイマー処理の実行"); // ディレクトリ削除イベントが発生している場合、 // 削除したディレクトリに含まれていたファイルを、削除したパスから見つけ出して削除処理を行うキューに追加する lock (sameDirectoryOperation_Locker) { if (sameDirectoryOperation_Name != "") { sameDirectoryOperation_Name = ""; var relativeDirPath = mWorkspace.TrimWorekspacePath(sameDirectoryOperation_FullPath); using (AsyncScopedLifestyle.BeginScope(container)) { var fileMappingInfoRepository = container.GetInstance <IFileMappingInfoRepository> (); foreach (var prop in fileMappingInfoRepository.FindPathWithStart(relativeDirPath)) { var fileUpdateQueueItem = new FileUpdateQueueItem { Target = new FileInfo(Path.Combine(mWatcherVirtual.Path, prop.MappingFilePath + ".aclgene")) }; fileUpdateQueueItem.Recents.Add(new RecentInfo { EventType = WatcherChangeTypes.Deleted }); mUpdatesWatchFiles.AddOrUpdate(prop.MappingFilePath, fileUpdateQueueItem, (_key, _value) => fileUpdateQueueItem); } } } } // foreach (var @pair in mUpdatesWatchFiles.ToList()) { // 最後のファイル監視状態から、一定時間経過している場合のみ処理を行う。 var @diff = DateTime.Now - @pair.Value.LastUpdate; if (@diff.Seconds >= 10) // 10秒 以上経過 { FileUpdateQueueItem item; // work if (mUpdatesWatchFiles.TryRemove(@pair.Key, out item)) { var @lastItem = item.Recents.LastOrDefault(); // NOTE: UpdateVirtualSpaceFlowワークフローを呼び出す mLogger.Info("ワークフロー実行 [{1}] 対象ファイルパス={0}", item.Target.FullName, @lastItem.EventType); // ワークフロー処理中に発生するファイル更新イベントにより、更新キューに項目が追加されてしまうことを防ぐため、 // 処理中のファイルを更新キューから除外するための除外リストに、処理中のファイルを追加する。 // // ※処理中のファイルがACLファイル以外の場合、対象ファイルのACLファイル名も除外リストに追加する mIgnoreUpdateFiles.Enqueue(item.Target.FullName); if (item.Target.Extension != ".aclgene") { var localPath = mWorkspace.TrimWorekspacePath(item.Target.FullName); var vpath = Path.Combine(mWorkspace.VirtualPath, localPath); mIgnoreUpdateFiles.Enqueue(vpath + ".aclgene"); // 仮想領域に作成するACLファイルを除外リストに追加 } using (var scope = FoxpictAsyncScopedLifestyle.BeginScope(container)) { try { var workspaceRepository = container.GetInstance <IWorkspaceRepository> (); var workspace = workspaceRepository.Load(mWorkspace.Id); if (workspace == null) { workspace = mWorkspace; } // VFS機能のサービスを取得する var fileUpdateRunner = container.GetInstance <IFileUpdateRunner> (); if (workspace.ContainsImport(item.Target)) { // インポート領域のファイルの場合は、カテゴリのパース処理を実施する。 fileUpdateRunner.EnableCategoryParse = true; } // 処理対象のファイルがACLファイルか、物理ファイルかで処理を切り分けます // ■ACLファイルの場合 // リネーム更新イベントに対応します。 // ■物理ファイルの場合 // リネーム更新イベントも、UPDATEイベントとして処理します。 if (item.Target.Extension == ".aclgene") { var fileNameWithputExtension = item.Target.Name.Replace(item.Target.Extension, ""); switch (@lastItem.EventType) { case WatcherChangeTypes.Renamed: fileUpdateRunner.file_rename_acl(item.Target, workspace); break; case WatcherChangeTypes.Changed: case WatcherChangeTypes.Created: fileUpdateRunner.file_create_acl(item.Target, workspace); break; case WatcherChangeTypes.Deleted: fileUpdateRunner.file_remove_acl(item.Target, workspace); break; } } else { if (File.Exists(item.Target.FullName)) { switch (@lastItem.EventType) { case WatcherChangeTypes.Renamed: case WatcherChangeTypes.Changed: case WatcherChangeTypes.Created: fileUpdateRunner.file_create_normal(item.Target, workspace); break; case WatcherChangeTypes.Deleted: break; } } else { mLogger.Info("「{0}」は存在しない物理ファイルのため、処理をスキップします。", item.Target.FullName); } } scope.Complete(); } catch (Exception expr) { mLogger.Error(expr, "VFSの処理に失敗しました。"); mLogger.Debug(expr.StackTrace); if (expr.InnerException != null) { mLogger.Error(expr.InnerException.Message); mLogger.Error(expr.InnerException.StackTrace); } } } // 処理を終了したファイルを、除外リストから削除します string ignoreUpdateFile; mIgnoreUpdateFiles.TryDequeue(out ignoreUpdateFile); if (item.Target.Extension != ".aclgene") { mIgnoreUpdateFiles.TryDequeue(out ignoreUpdateFile); } } } // [CPU使用率に対するループ遅延を行う] // var cpuPer = _CpuCounter.NextValue(); // if (cpuPer > 90.0) // { // await Task.Delay(100); // 100msec待機 // } // else if (cpuPer > 30.0) // { // //await Task.Delay(10); // 10msec待機 // } } // [FileWatcherコンポーネントの、ファイルイベント通知漏れ対策] // 1つでもファイルイベントが発生しているフォルダは、そのフォルダに属するファイルを処理キューに「新規作成アイテム」として登録する。 // ただし、処理済みのファイルまで登録してしまうと2度処理を行うことになるため、除外リストに含まれているファイルは処理キューへの登録は行わない。 foreach (var pair in mUpdateDirectory.ToList()) { var itemDirectory = new DirectoryInfo(pair.Value.FullName); if (mWorkspace.ContainsImport(itemDirectory)) { // フォルダが、インポート領域の場合 if (!itemDirectory.Exists) { // インポート領域では、Deleteイベントは受け取らないため、 // フォルダ有無を検証し、フォルダが削除済みの場合は更新ディレクトリキューから削除する。 DirectoryInfo dummy; mUpdateDirectory.TryRemove(pair.Key, out dummy); mLogger.Info($"キューから処理対象のディレクトリ({dummy.Name})を削除"); continue; } RegisterDirectoryFiles(itemDirectory); // インポート領域内で、かつ空フォルダの場合は、フォルダを削除する if (itemDirectory.GetFiles("*", SearchOption.AllDirectories).Length == 0) { try { itemDirectory.Delete(true); } catch (Exception expr) { mLogger.Error(expr, $"フォルダの削除({itemDirectory.Name})に失敗しました。"); throw expr; } mLogger.Info($"インポート領域内のディレクトリ({itemDirectory.Name})を削除しました。"); DirectoryInfo dummy; mUpdateDirectory.TryRemove(pair.Key, out dummy); mLogger.Info($"キューから処理対象のディレクトリ({dummy.Name})を削除"); } } else { // フォルダが、仮想領域の場合 RegisterDirectoryFiles(itemDirectory); DirectoryInfo dummy; mUpdateDirectory.TryRemove(itemDirectory.FullName, out dummy); } } timer.Enabled = true; }
/// <summary> /// [LLD-03-05-01:01-01-01] /// </summary> public void file_create_normal(FileUpdateQueueItem item, IWorkspace workspace) { // 1. 対象ファイルが存在するかチェック if (!item.Target.Exists) { throw new ApplicationException("対象ファイルが指定位置に存在しません。"); } // 2. 対象ファイルを、物理空間に移動する(ディレクトリが無い場合は、ディレクトリも作成する) // 一時ファイル名を使用する。 var aclfileLocalPath_Update = workspace.TrimWorekspacePath(item.Target.FullName); // 移動先のディレクトリがサブディレクトリを含む場合、 // 存在しないサブディレクトリを作成します。 var newFileInfo = new FileInfo(Path.Combine(workspace.PhysicalPath, aclfileLocalPath_Update)); Directory.CreateDirectory(newFileInfo.Directory.FullName); var fromFilePath = new FileInfo(Path.Combine(workspace.VirtualPath, aclfileLocalPath_Update)); var toFilePath = new FileInfo(Path.Combine(workspace.PhysicalPath, aclfileLocalPath_Update + ".tmp")); File.Move(fromFilePath.FullName, toFilePath.FullName); // 3. ACLファイルを仮想空間に作成する var aclfilepath = Path.Combine(workspace.VirtualPath, aclfileLocalPath_Update) + ".aclgene"; // ACLファイルの作成を行います。 string aclhash = VfsLogicUtils.GenerateACLHash(); var data = new AclFileStructure(); data.Version = AclFileStructure.CURRENT_VERSION; data.LastUpdate = DateTime.Now; data.Data = new KeyValuePair <string, string>[] { new KeyValuePair <string, string>("ACLHASH", aclhash) }; using (var file = File.Create(aclfilepath)) { Serializer.Serialize(file, data); } // 4. ファイルマッピング情報を作成し、データベースに格納する var entity = mFileMappingInfoRepository.New(); entity.AclHash = aclhash; entity.SetWorkspace(workspace); entity.Mimetype = "image/png"; // 未実装(テスト実装) entity.MappingFilePath = aclfileLocalPath_Update; entity.LostFileFlag = false; mFileMappingInfoRepository.Save(); // 5. 一時ファイル名を、正しいファイル名にリネームする if (!File.Exists(toFilePath.FullName)) { throw new ApplicationException(toFilePath.FullName + "が見つかりません"); } var extFileName = Path.GetFileNameWithoutExtension(toFilePath.Name); toFilePath.MoveTo(Path.Combine(toFilePath.DirectoryName, extFileName)); // 6. コンテンツ情報の作成(Category作成→Content作成→タグ・ラベル作成) var content = UpdateContentFromFileMapping(entity); // 7. サムネイル作成 GenerateArtifact(content, workspace); }