3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C# - 文書洗浄 - タイムスタンプ更新とプロパティ削除

Last updated at Posted at 2025-04-20

はじめに

公開文書、外部送付ファイルは、スッキリ洗浄してから提供することがありました。
サンプルソースは、処理毎で2つに分離しましたが、ひとつにまとめてツール化していました。

テスト環境

ここに記載した情報/ソースコードは、Visual Studio Community 2022 を利用した下記プロジェクトで生成したモジュールを Windows 11 24H2 で動作確認しています。

  • Windows Forms - .NET Framework 4.8
  • Windows Forms - .NET 8
  • WPF - .NET Framework 4.8
  • WPF - .NET 8

Visual Studio 2022 - .NET Framework 4.8 は、C# 7.3 が既定です。
このため、サンプルコードは、基本的に C# 7.3 機能範囲で記述しています。

タイムスタンプ更新

複数ファイルの場合、タイムスタンプがそれぞれ異なる状態よりも、揃っていたほうが良いと思い、全て同一日時のタイムスタンプとしていました。

休日、もしくは、定時勤務時間以外のタイムスタンプは、それ自体が勤務情報として認知されることもあります。

サンプルコード

var folder = "FOLDER_PATH";                // TODO
var today = DateTime.Today.AddHours(10);   // 本日の 10:00:00
SetTimeStamp(folder, today);
// フォルダ下のファイルについて、タイムスタンプを更新
private void SetTimeStamp(string target, DateTime today)
{
  // サブフォルダ下も処理する場合はコメントを外す
  // var folders = Directory.GetDirectories(target);
  // if (folders != null)
  // {
  //   foreach(var folder in folders)
  //   {
  //     SetTimeStamp(folder, today);
  //   }
  // }

  // フォルダ内のファイル取得
  var files = Directory.GetFiles(target);
  if (files != null)
  {
    foreach (var file in files)
    {
      // タイムスタンプ更新
      File.SetCreationTime(file, today);
      File.SetLastWriteTime(file, today);
    }
  }
}

プロパティ削除

文書プロパティに個人情報などが格納されている場合、クリアしたほうが良いと思います。
本記事では、下記文書を対象とします。

  • Microsoft Office 文書(Word, Excel, Power Point)
  • PDF 文書

Microsoft Office 文書は、NuGet Gallery | DocumentFormat.OpenXml を利用します。
PDF 文書は、AGPL は避けて、NuGet Gallery | PDFsharp を利用します。
PDFSharp は Version 1.50.5147 までが .NET Framework 用で、Version 6.0.0 以降が .NET 用となっています。.NET Framework で利用する場合は、バージョン指定して取得しましょう。

  • .NET
    • PM> NuGet\Install-Package DocumentFormat.OpenXml
    • PM> NuGet\Install-Package PDFsharp
  • .NET Framework
    • PM> NuGet\Install-Package DocumentFormat.OpenXml
    • PM> NuGet\Install-Package PdfSharp -Version 1.50.5147

Microsoft Office 文書

エクスプローラ、プロパティで参照できる下記情報の削除を行います。

office.png

Word、Excel、Power Point で利用するクラスが異なりますが、それ以外のコードは同一です。

文書 利用するクラス
Word WordprocessingDocument
Excel SpreadsheetDocument
Power Poinet PresentationDocument

Word サンプルコードを直接掲載して、それ以外はアコーディオンで掲載します。
サンプルコードで、削除不要なプロパティがありましたら、コメントアウトしてください。

using DocumentFormat.OpenXml.Packaging;
// Word 文書プロパティ削除
private void CleanupWordProperties(string file)
{
  // Word ファイルを開く
  using (var document = WordprocessingDocument.Open(file, true))
  {
    // ビルトインプロパティ
    var coreProperties = document.PackageProperties;
    if (coreProperties != null)
    {
      // プロパティを null に設定して削除
      coreProperties.Title = null;           // タイトル
      coreProperties.Subject = null;         // 件名
      coreProperties.Keywords = null;        // タグ
      coreProperties.Category = null;        // 分類項目
      coreProperties.Description = null;     // コメント
      coreProperties.Creator = null;         // 作成者
      coreProperties.LastModifiedBy = null;  // 最終保存者
      coreProperties.Revision = null;        // 改定番号 
      coreProperties.Modified = null;        // 最終更新日時
      coreProperties.LastModifiedBy = null;  // 前回保存者
      coreProperties.Created = null;         // コンテンツの作成日時
      coreProperties.Modified = null;        // 前回保存日時
      coreProperties.LastPrinted = null;     // 前回印刷日
    }
    // 拡張プロパティ
    var extendedProperties = document.ExtendedFilePropertiesPart?.Properties;
    if (extendedProperties != null)
    {
      // プロパティを null に設定して削除
      extendedProperties.Company = null;      // 会社
      extendedProperties.Manager = null;      // マネージャ
      extendedProperties.TotalTime = null;    // 編集総時間
      extendedProperties.Save();
    }
  }
}

コンテンツの作成日時、前回保存日時を削除した場合、それぞれ、対象ファイルのタイムスタンプ CreationTime, LastWriteTime を参照するみたいです。
前述、タイムスタンプ更新とあわせて対処することで、表示情報のリセットは可能です。

Excel 文書プロパティ削除
// Excel 文書プロパティ削除
private void CleanupExcelProperties(string file)
{
  // Excel ファイルを開く
  using (var document = SpreadsheetDocument.Open(file, true))
  {
    // ビルトインプロパティ
    var coreProperties = document.PackageProperties;
    if (coreProperties != null)
    {
      // プロパティを null に設定して削除
      coreProperties.Title = null;           // タイトル
      coreProperties.Subject = null;         // 件名
      coreProperties.Keywords = null;        // タグ
      coreProperties.Category = null;        // 分類項目
      coreProperties.Description = null;     // コメント
      coreProperties.Creator = null;         // 作成者
      coreProperties.LastModifiedBy = null;  // 最終保存者
      coreProperties.Revision = null;        // 改定番号 
      coreProperties.Modified = null;        // 最終更新日時
      coreProperties.LastModifiedBy = null;  // 前回保存者
      coreProperties.Created = null;         // コンテンツの作成日時
      coreProperties.Modified = null;        // 前回保存日時
      coreProperties.LastPrinted = null;     // 前回印刷日
    }
    // 拡張プロパティ
    var extendedProperties = document.ExtendedFilePropertiesPart?.Properties;
    if (extendedProperties != null)
    {
      // プロパティを null に設定して削除
      extendedProperties.Company = null;      // 会社
      extendedProperties.Manager = null;      // マネージャ
      extendedProperties.TotalTime = null;    // 編集総時間
      extendedProperties.Save();
    }
  }
}
Power Point 文書プロパティ削除
// Power Point 文書プロパティ削除
private void CleanupPowerPointProperties(string file)
{
  // Power Point ファイルを開く
  using (var document = PresentationDocument.Open(file, true))
  {
    // ビルトインプロパティ
    var coreProperties = document.PackageProperties;
    if (coreProperties != null)
    {
      // プロパティを null に設定して削除
      coreProperties.Title = null;           // タイトル
      coreProperties.Subject = null;         // 件名
      coreProperties.Keywords = null;        // タグ
      coreProperties.Category = null;        // 分類項目
      coreProperties.Description = null;     // コメント
      coreProperties.Creator = null;         // 作成者
      coreProperties.LastModifiedBy = null;  // 最終保存者
      coreProperties.Revision = null;        // 改定番号 
      coreProperties.Modified = null;        // 最終更新日時
      coreProperties.LastModifiedBy = null;  // 前回保存者
      coreProperties.Created = null;         // コンテンツの作成日時
      coreProperties.Modified = null;        // 前回保存日時
      coreProperties.LastPrinted = null;     // 前回印刷日
    }
    // 拡張プロパティ
    var extendedProperties = document.ExtendedFilePropertiesPart?.Properties;
    if (extendedProperties != null)
    {
      // プロパティを null に設定して削除
      extendedProperties.Company = null;      // 会社
      extendedProperties.Manager = null;      // マネージャ
      extendedProperties.TotalTime = null;    // 編集総時間
      extendedProperties.Save();
    }
  }
}

PDF 文書

PDF は提供時に PDF 化していたこともあり、文書プロパティをあまり気にしていませんでしたが、Microsoft Office 文書と同様に対処すべきだったと思い、本記事では掲載することにしました。
PDF編集ソフト(本来は Acrobat Pro で確認すべきですがライセンスを持っていないので、CubePDF Utility を利用)で参照できる下記情報の削除を行います。

pdf.png

using PdfSharp.Pdf.IO;
// PDF 文書プロパティ削除
private void CleanupPdfProperties(string target)
{
  // PDF ファイルを開く
  using (var document = PdfReader.Open(target, PdfDocumentOpenMode.Modify))
  {
    // プロパティを string.Empty に設定して削除
    document.Info.Title = string.Empty;       // タイトル
    document.Info.Author = string.Empty;      // 作成者
    document.Info.Subject = string.Empty;     // サブタイトル
    document.Info.Keywords = string.Empty;    // キーワード

    // PDF編集ソフトによっては、下記も参照できるので、必要に応じてコメントアウトを外す
    // document.Info.Comment = string.Empty;
    // document.Info.Creator = string.Empty;
    // document.Info.CreationDate = DateTime.Now;
    // document.Info.ModificationDate = DateTime.Now;

    document.Save(target);
  }
}

全体処理

フォルダ下の対象文書を処理する全体処理のソースコードを掲載します。

var folder = "FOLDER_PATH";                // TODO
ClearDocumentProperties(folder);
// フォルダ下の文書について、文書プロパティを削除
private void ClearDocumentProperties(string target)
{
  // サブフォルダ下も処理する場合はコメントを外す
  // var folders = Directory.GetDirectories(target);
  // if (folders != null)
  // {
  //   foreach(var folder in folders)
  //   {
  //     ClearDocumentProperties(folder);
  //   }
  // }

  // フォルダ内のファイル取得
  var files = Directory.GetFiles(target);
  if (files != null)
  {
    foreach (var file in files)
    {
      // 拡張子を利用した分岐
      var fileType = Path.GetExtension(file)?.ToLower();
      switch (fileType)
      {
        case ".docx":
          CleanupWordProperties(file);
          break;
        case ".xlsx":
          CleanupExcelProperties(file);
          break;
        case ".pptx":
          CleanupPowerPointProperties(file);
          break;
        case ".pdf":
          CleanupPdfProperties(file);
          break;
      }
    }
  }
}

.NET 8 で利用可能な switch 式を用いると、こんな感じですかね。

// 拡張子を利用した分岐 - switch 式
var fileType = Path.GetExtension(file)?.ToLower();
Action<string>? action = fileType switch
{
  ".docx" => CleanupWordProperties,
  ".xlsx" => CleanupExcelProperties,
  ".pptx" => CleanupPowerPointProperties,
  ".pdf" => CleanupPdfProperties,
  _ => null
};
// 拡張子に基づく action を実行
action?.Invoke(file);

.NET Framework でも、Visual Studio と C#、および、フレームワーク(プラットフォーム)の関係 で記載した C# バージョン設定で 8.0 以降にすれは、上記 switch 式を利用できます。
※null 許容参照型の明示は不要なので、下記修正は必要です。

// Action<string>? を Action<string> に修正
Action<string> action = fileType switch
3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?