💡 مثال کامل قابل اجرا در گیت‌هاب موجود است:
scrub-office-document-pii-dotnet

The Old Way Was Painful

روال کار به این شکل بود. سند را باز می‌کنید، File → Info → Check for Issues → Inspect Document، گزینه‌ها را تیک می‌زنید، Remove All را می‌زنید، زیر نام جدید ذخیره می‌کنید، می‌بندید، سند بعدی را باز می‌کنید. پس از چهل فایل، کسی متوجه می‌شود که Inspect Document همچنین عنوان (Title) که کلیدهای ایندکس رکوردها روی آن است را حذف کرده، و اینکه نسخه‌ای که یک ساعت پیش ارسال شده بود هنوز شناسه تأییدکننده SharePoint را دارد، چون فایل از برنامه‌ای متفاوت ذخیره شده بود که فیلد را دوباره نوشت.

حذف PII متادیتا یک قابلیت GroupDocs.Metadata برای .NET است که ویژگی‌های حاوی هویت را از اسناد Office به‌صورت برنامه‌نویسی حذف می‌کند و پس از آن آنچه باقی می‌ماند را گزارش می‌دهد. روش دستی در سه مورد ناکام می‌ماند: برای تعداد کمی از فایل‌ها مقیاس‌پذیر نیست، دربارهٔ اینکه کدام فیلدها حذف شوند همه یا هیچ است، و هیچ رکوردی از آنچه حذف شده تولید نمی‌کند. این مقاله نسخهٔ .NET همان کار را، یک گروه ویژگی به‌صورت جداگانه، نشان می‌دهد.

کمک می‌کند بدانیم در واقع چه چیزی داخل سند است. یک فایل Word که از یک دور بازبینی عبور کرده معمولاً شامل Author و LastSavedBy از حساب ویندوز کسی که آن را ذخیره کرده، Manager و Company از قالب شرکتی، شمارندهٔ بازنگری، TotalEditingTime، یک زمان‌مهر LastPrinted و شمارنده‌های رشتهٔ نظرات است. اگر زنجیرهٔ SharePoint هم اضافه شود، شناسه‌های تأییدکننده، مسیرهای گردش کار، URIهای نوع محتوا و قالبی که سند از آن ساخته شده نیز به‌دست می‌آید. هیچ‌یک از این‌ها روی صفحه قابل مشاهده نیست و همه در همان فایل حمل می‌شوند.

There’s a Better Way

همهٔ عملکردهای GroupDocs.Metadata برای .NET از یک موتور جستجوی ویژگی عبور می‌کنند. RemoveProperties یک لامبدا بر روی MetadataProperty می‌گیرد، هر ویژگی که لامبدا بپذیرد حذف می‌کند و تعداد حذف‌شده‌ها را برمی‌گرداند. FindProperties همان لامبدا را بدون نوشتن اجرا می‌کند. ویژگی‌ها همچنین برچسب دارند، بنابراین Tags.Person.Creator فیلدهای سبک نویسنده را در قالب‌ها و بسته‌های مختلف شناسایی می‌کند به‌جای مقایسهٔ نام‌های دقیق که در هر برنامه متفاوتند.

این باعث می‌شود سه شکل پاک‌سازی به‌جای یک دکمه داشته باشید: یک عبور برچسب برای فیلدهای هویتی، عبور نام برای خانواده‌هایی مثل نظرات و بازنگری‌ها، و Sanitize() وقتی که هیچ‌چیزی نباید بماند. هر سه عددی برمی‌گردانند و این اعداد همان چیزی است که عبور را قابل حسابرسی می‌کند.

The New Way: A Predicate per Property Group

Step 1 - Clear the names

چهار بررسی برچسب گروه هویت را پوشش می‌دهند. فیلدهای توصیفی دست‌نخورده می‌مانند، که این تفاوت با گزینهٔ Remove All در Document Inspector است:

using (var metadata = new Metadata(inputPath))
{
    if (metadata.FileFormat == FileFormat.Unknown) return 0;
    var affected = metadata.RemoveProperties(p =>
        p.Tags.Contains(Tags.Person.Creator) ||
        p.Tags.Contains(Tags.Person.Editor) ||
        p.Tags.Contains(Tags.Person.Manager) ||
        p.Tags.Contains(Tags.Corporate.Company));
    metadata.Save(outputPath);
    return affected;
}

بررسی FileFormat.Unknown نگهبانی است که مقدار صفر را صادق نگه می‌دارد: بدون آن، یک فایل غیرقابل خواندن و یک فایل تمیز به‌نظر یکسان می‌آیند.

Step 2 - Clear the families around them

نظرات، بازنگری‌ها و فیلدهای سرور برچسب ندارند، بنابراین شرط بر اساس نام‌ها مطابقت می‌دهد. خط زمان ویرایش گروهی است که بیشتر فراموش می‌شود و همان گروهی است که نشان می‌دهد سند چگونه تولید شده:

using (var metadata = new Metadata(inputPath))
{
    if (metadata.FileFormat == FileFormat.Unknown) return 0;
    var affected = metadata.RemoveProperties(p =>
        p.Name != null && (
            p.Name.Contains("Revision") ||
            p.Name.Contains("TrackedChange") ||
            p.Name.Contains("LastPrinted") ||
            p.Name.Contains("TotalEditingTime") ||
            p.Name.Contains("EditTime")));
    metadata.Save(outputPath);
    return affected;
}

مطابقت زیررشته‌ای عمدی است: CommentsCount را همراه با Comment و TotalEditingTime را همراه با EditTime می‌گیرد، بدون اینکه نیاز به نگهداری فهرست نام‌های دقیق برای هر قالب باشد. عبور SharePoint همان فراخوانی با Server، Workflow، Approver، ContentType و Template است.

Step 3 - Wipe everything, then check the result

در مرز اعتماد، یک فراخوانی چهار عبور را جایگزین می‌کند:

using (var metadata = new Metadata(inputPath))
{
    if (metadata.FileFormat == FileFormat.Unknown) return 0;
    var affected = metadata.Sanitize();
    metadata.Save(outputPath);
    return affected;
}

سپس بخشی که روش دستی معادل آن را ندارد. اسکن تأییدیه پیش‌شرط‌های حذف را از طریق FindProperties دوباره استفاده می‌کند و بازماندگان را به دو فهرست تقسیم می‌کند:

foreach (var p in props)
{
    var value = p.InterpretedValue?.ToString() ?? p.Value?.ToString() ?? string.Empty;
    if (string.IsNullOrWhiteSpace(value)) continue;
    if (value == "0" || value == "0.0") continue;

    var entry = $"{p.Name}={value}";
    var name = p.Name ?? string.Empty;
    if (name.StartsWith("Comment") || name.StartsWith("Revision"))
        report.ContentLevelLeaks.Add(entry);
    else
        report.MetadataLeaks.Add(entry);
}

MetadataLeaks باید قبل از اینکه فایلی به‌عنوان پاک‌سازی‌شده شمارش شود خالی باشد. ContentLevelLeaks اطلاعاتی است: نظرات Word و نویسندگان تغییرات ردیابی‌شده در word/document.xml قرار دارند، که محتوای بدنه است و پاک‌سازی آن نیاز به کتابخانهٔ ویرایش محتوا مانند Aspose.Words دارد نه به یک API متادیتا.

Why not just call Sanitize on everything?

چون اکثر اسناد هنوز در حال استفاده هستند. Sanitize() هر بستهٔ شناسایی‌شده را پاک می‌کند و این شامل Title، Subject و Keywords می‌شود، فیلدهایی که سیستم‌های رکورد و ایندکس جستجو به آن‌ها وابسته‌اند. در حالی که فایل به‌صورت داخلی در گردش است، از عبورهای هدفمند استفاده کنید، متادیتای توصیفی را فعال نگه دارید و پاک‌سازی کامل را برای نسخه‌ای که واقعاً سازمان را ترک می‌کند رزرو کنید.

Side-by-Side: Before vs. After

بازرسی دستی GroupDocs.Metadata برای .NET
قابلیت انتخاب حذف همه، شامل فیلدهای توصیفی یک شرط برای هر گروه ویژگی
پوشش فیلدهایی که دیالوگ نمایش می‌دهد هر بسته‌ای که کتابخانه شناسایی می‌کند، شامل بخش‌های سفارشی OOXML
ثبت هیچ تعداد موارد تحت تأثیر که برای هر عملیات برگردانده می‌شود
تأیید دوباره باز کنید و نگاه کنید اسکن FindProperties با فهرست‌های متادیتا و سطح محتوا
دسته‌ای از ۲۰۰ فایل ۲۰۰ کلیک یک حلقه، پنج عملیات، یک خط لاگ برای هر فایل

سطر تغییر رفتار، سطر ثبت است. وقتی هر عبور عددی برمی‌گرداند، پاک‌سازی دیگر گامی نیست که کسی باید به‌خاطر بسپارد، بلکه داده‌ای است که خط لوله می‌تواند بر آن استوار باشد: آستانه‌ای در تست، فیلدی در جدول حسابرسی، شرطی که کار شبانه را شکست می‌دهد. این همان سطری است که یک روش دستی نمی‌تواند در هیچ سطحی از انضباط تولید کند.

Real-World Example: The Pre-Send Hook

یک پورتال پشتیبانی به کارکنان اجازه می‌دهد اسناد را به تیکت‌های مشتری پیوست کنند. پردازش‌کنندهٔ پیوست اکنون عبور هویت و عبور سرور را قبل از ذخیره‌سازی فایل اجرا می‌کند، هر دو شمارش را در مقابل تیکت ثبت می‌کند و بررسی نشت را روی نسخهٔ ذخیره‌شده انجام می‌دهد. فهرست خالی‌نه‌بودن نشت متادیتا بارگذاری را با پیامی که نام ویژگی مخرب را می‌گوید رد می‌کند، بنابراین شخصی که فایل را پیوست می‌کند بلافاصله متوجه می‌شود نه پس از رسیدن به مشتری.

دو جزئیات این هوک را عملی می‌کند. عبورها به مسیر جدید می‌نویسند، بنابراین اصل فایل در ذخیره‌سازی شخص باقی می‌ماند و هیچ‌چیزی توسط گام خودکار خراب نمی‌شود. و شمارش‌ها در رکورد تیکت کنار پیوست ذخیره می‌شوند، به این معنی که پاسخ به «چه چیزی از این سند حذف شد» عددی ذخیره‌شده است نه فرضی دربارهٔ کاری که خط لوله معمولاً انجام می‌دهد.

اولین باری که این بررسی را روی یک قالب واقعی انجام دادم، مقدار Manager را برگرداند که عبور هویت ثانیه‌ها پیش آن را حذف کرده بود و قالب شرکتی بلافاصله پس از ذخیره‌سازی دوباره نوشته بود. فراخوانی حذف دقیقاً همان‌طور که مستند شده بود کار می‌کرد؛ مشکل در خط لوله اطراف آن بود و فقط خواندن‑باز آن نشان داد.

What Else Can You Do with GroupDocs.Metadata?

موتور شرطی همان‌طور که می‌خواند. مقایسهٔ ویژگی‌ها بین دو نسخهٔ یک سند تغییرات مالکیت و بازنویسی خارج از فرآیند بازبینی را نشان می‌دهد و مروری بر پاک‌سازی متادیتا جایی را نشان می‌دهد که ابزار تعاملی هنوز در کنار عبور مبتنی بر API جای دارد. چون سیستم برچسب‌ها فرمت‌ها را پوشش می‌دهد، شرط هویت نوشته‌شده در اینجا بر روی PDFها، تصاویر و فایل‌های صوتی بدون تغییر اجرا می‌شود.

این قابلیت حمل‌پذیری ارزش برنامه‌ریزی دارد. یک قانون پاک‌سازی نوشته‌شده به‌صورت لامبدا بر روی MetadataProperty کد C# معمولی است، بنابراین می‌تواند در یک کتابخانهٔ مشترک زندگی کند، در مقابل اسناد نمونه واحد تست شود و توسط هر سرویسی که به آن نیاز دارد اعمال شود: نقطهٔ انتهای خروجی، کار زمان‌بندی‌شدهٔ رکوردها، یا گام ساختی که پیش از انتشار اسناد پیوست‌شده را پاک‌سازی می‌کند. قوانین در یک مکان می‌مانند؛ فقط نقاط فراخوانی تغییر می‌کنند.

Conclusion

چهار عبور هدفمند، یک پاک‌سازی کامل، یک اسکن تأییدیه. این مجموعه محدودهٔ عملی برای اسناد Office را پوشش می‌دهد: متادیتای توصیفی را در حالی که فایل در گردش است حفظ کنید، همه چیز را وقتی که فایل خارج می‌شود پاک کنید و نتیجه را به‌هر دو شکل ثابت کنید. نمونه را کلون کنید، آن را روی سندی که از یک دور بازبینی واقعی عبور کرده اجرا کنید و شمارش‌های تحت تأثیر را بخوانید. معمولاً بیش از حد انتظار هستند و این همان هدف است.

Additional Resources