💡 مثال کامل قابل اجرا در GitHub موجود است:
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 باید قبل از اینکه فایلی به‌عنوان sanitized شناخته شود خالی باشد. 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
انتخاب‌پذیری Remove All، شامل فیلدهای توصیفی یک پیش‌شرط برای هر گروه ویژگی
پوشش فیلدهایی که دیالوگ نشان می‌دهد هر بسته‌ای که کتابخانه شناسایی می‌کند، شامل بخش‌های سفارشی OOXML
رکورد هیچ‌کدام تعداد تأثیر‌گذاری‌شده برای هر عملیات
تأیید باز کردن مجدد و نگاه کردن اسکن FindProperties با فهرست‌های متادیتا و سطح محتوا
دستهٔ 200 فایل 200 کلیک‌گذاری یک حلقه، پنج عملیات، یک خط لاگ برای هر فایل

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

Real-World Example: The Pre-Send Hook

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

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

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

What Else Can You Do with GroupDocs.Metadata?

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

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

Conclusion

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

Additional Resources