💡 مثال كامل يعمل متاح على GitHub:
scrub-office-document-pii-dotnet

الطريقة القديمة كانت مؤلمة

تسير الروتين هكذا. افتح المستند، ملف، معلومات، فحص المشكلات، فحص المستند، ضع علامة على الصناديق، إزالة الكل، احفظ باسم جديد، أغلق، افتح التالي. بعد أربعين ملفًا يلاحظ أحدهم أن فحص المستند أيضًا أزال العنوان الذي يعتمد عليه فهرس السجلات، وأن النسخة التي أُرسلت قبل ساعة لا تزال تحمل معرف موافق SharePoint، لأن الملف تم حفظه من تطبيق مختلف أعاد كتابة الحقل.

إزالة بيانات التعريف الشخصية (PII) هي قدرة GroupDocs.Metadata لـ .NET تحذف الخصائص التي تحمل هوية من مستندات Office برمجيًا وتُبلغ عما يبقى بعد ذلك. الروتين اليدوي يفشل في ثلاث نقاط: لا يتوسع إلى ما بعد عدد قليل من الملفات، هو إما كل شيء أو لا شيء بالنسبة للحقول التي تُحذف، ولا ينتج سجلًا لما تم إزالته. تُظهر هذه المقالة نسخة .NET من نفس العمل، مجموعة خصائص واحدة في كل مرة.

من المفيد معرفة ما هو موجود فعليًا. ملف Word مر بجولة مراجعة عادةً يحمل Author و LastSavedBy من حساب Windows للذي حفظه، Manager و Company من القالب المؤسسي، عداد مراجعات، TotalEditingTime، طابع زمني LastPrinted، وعدادات لسلاسل التعليقات. إضافة SharePoint إلى السلسلة يمنحك أيضًا معرفات الموافقة، مسارات سير العمل، عناوين URI لنوع المحتوى، والقالب الذي تم إنشاء المستند منه. لا شيء من ذلك يُرى على الصفحة، وكل ذلك ينتقل داخل نفس الملف.

هناك طريقة أفضل

كل شيء في GroupDocs.Metadata لـ .NET يمر عبر محرك بحث خصائص واحد. RemoveProperties يأخذ دالة لامبدا على MetadataProperty، يحذف كل خاصية تقبلها الدالة، ويُعيد العدد. FindProperties يُنفّذ نفس الدالة دون كتابة. الخصائص تحمل أيضًا وسومًا، لذا Tags.Person.Creator يحدد الحقول ذات نمط المؤلف عبر الصيغ والحزم بدلاً من مطابقة الأسماء الحرفية التي تختلف حسب التطبيق المنتج.

هذا يمنح ثلاث أشكال من التنظيف بدلاً من زر واحد: تمريرة وسوم للحقول الهوية، تمريرات أسماء للعائلات مثل التعليقات والمراجعات، وSanitize() عندما لا يجب أن يبقى شيء. الثلاثة تُعيد أعدادًا، وهذه الأعداد هي ما يجعل العملية قابلة للتدقيق.

الطريقة الجديدة: شرط لكل مجموعة خصائص

الخطوة 1 - مسح الأسماء

أربعة فحوصات وسوم تغطي مجموعة الهوية. الحقول الوصفية تُترك دون تعديل، وهذا هو الفرق عن “إزالة الكل” في فاحص المستند:

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 هو الحارس الذي يحافظ على الصدق في الصفر: بدون هذا الفحص، يبدو الملف غير القابل للقراءة والملف النظيف متشابهين للمتصل.

الخطوة 2 - مسح العائلات حولها

التعليقات، المراجعات، وحقول الخادم لا تملك وسمًا، لذا يتطابق الشرط مع الأسماء بدلاً من ذلك. خط الزمن التحريري هو المجموعة التي تُنسى غالبًا، وهو ما يوضح كيف تم إنتاج المستند:

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.

الخطوة 3 - مسح كل شيء، ثم فحص النتيجة

على حدود الثقة، استدعاء واحد يحل محل الأربع تمريرات:

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 بدلاً من واجهة برمجة تطبيقات الميتاداتا.

لماذا لا نستدعي Sanitize على كل شيء؟

لأن معظم المستندات لا تزال قيد الاستخدام. Sanitize() يمسح كل حزمة مكتشفة، ويشمل ذلك العنوان، الموضوع، والكلمات المفتاحية، وهي الحقول التي يعتمد عليها نظام السجلات وفهرس البحث. استخدم التمريرات المستهدفة بينما الملف يدور داخليًا، حافظ على الميتاداتا الوصفية، واحتفظ بالمسح الكامل للنسخة التي تغادر المنظمة فعليًا.

مقارنة جنبًا إلى جنب: قبل vs. بعد

الفحص اليدوي GroupDocs.Metadata for .NET
الانتقائية إزالة الكل، تشمل الحقول الوصفية شرط واحد لكل مجموعة خصائص
التغطية الحقول التي يعرضها الحوار كل حزمة يكتشفها المكتبة، بما في ذلك أجزاء OOXML المخصصة
السجل لا شيء عدد المتأثرين يُرجع لكل عملية
التحقق إعادة الفتح والنظر مسح FindProperties مع قوائم الميتاداتا ومستوى المحتوى
دفعة من 200 ملف 200 نقرة حلقة واحدة، خمس عمليات، سطر سجل واحد لكل ملف

الصف الذي يغيّر السلوك هو السجل. بمجرد أن تُعيد كل تمريرة عددًا، يتوقف التنظيف عن كونه خطوة يتذكرها شخص ما ويصبح بيانات يمكن للمسار أن يتحقق منها: عتبة في اختبار، حقل في جدول تدقيق، شرط يُفشل وظيفة ليلية. وهذا أيضًا هو الصف الذي لا يستطيع الروتين اليدوي إنتاجه بأي مستوى من الانضباط.

مثال واقعي: الخطاف قبل الإرسال

بوابة دعم تسمح للموظفين بإرفاق مستندات بتذاكر العملاء. الآن يُشغّل معالج المرفقات تمريرة الهوية وتمرير الخادم قبل حفظ الملف، ويسجّل كلا العدّين مقابل التذكرة، ويُجري فحص التسرب على النسخة المحفوظة. قائمة تسرب الميتاداتا غير الفارغة ترفض التحميل برسالة تسمي الخاصية المخالفة، بحيث يكتشف الشخص المرفق الملف ذلك فورًا بدلاً من بعد وصوله للعميل.

تفصيلان يجعلان هذا الخطاف عمليًا. التمريرات تُكتب إلى مسار جديد، لذا يبقى الأصلي في تخزين الموظف ولا يُدمّر شيء بخطوة آلية. والعدّات تُدخل في سجل التذكرة بجانب المرفق، مما يعني أن الإجابة على “ما الذي أُزيل من هذا المستند” هي رقم مخزن وليس افتراضًا حول ما يفعله المسار عادةً.

في المرة الأولى التي وجهت فيها هذا الفحص إلى قالب حقيقي، عادت بقيمة Manager التي أزالتها تمريرة الهوية قبل ثوانٍ وكان القالب المؤسسي قد كتبها مرة أخرى عند الحفظ. كان استدعاء الإزالة يعمل تمامًا كما هو موثق؛ المشكلة كانت في المسار حوله، ولا يظهر ذلك إلا عند القراءة مرة أخرى.

ماذا يمكنك أن تفعل أيضًا مع GroupDocs.Metadata؟

محرك الشرط نفسه يقرأ. مقارنة الخصائص بين نسختين من مستند تكشف عن تغييرات الملكية وإعادة التأليف خارج عملية المراجعة، ونظرة عامة على تنظيف الميتاداتا تغطي أين لا يزال الأداة التفاعلية تناسب بجانب تمريرة مدفوعة بـ API. لأن نظام الوسوم يمتد عبر الصيغ، فإن شرط الهوية المكتوب هنا يعمل أيضًا ضد ملفات PDF، الصور، والملفات الصوتية دون تعديل.

هذه القابلية للنقل تستحق التخطيط لها. قاعدة تنظيف مكتوبة كدالة لامبدا على MetadataProperty هي C# عادية، لذا يمكن أن تعيش في مكتبة مشتركة، تُختبر وحدةً ضد مستندات ثابتة، وتُطبق من قبل أي خدمة تحتاجها: نقطة تصدير، وظيفة سجلات مجدولة، أو خطوة بناء تمسح مرفقات الوثائق قبل الإصدار. القواعد تبقى في مكان واحد؛ فقط مواقع الاستدعاء تتغير.

الخلاصة

أربع تمريرات مستهدفة، مسح كامل واحد، فحص تحقق واحد. تغطي هذه المجموعة النطاق العملي لمستندات Office: حافظ على الميتاداتا الوصفية بينما الملف في التداول، امسح كل شيء عندما يغادر، وأثبت النتيجة بأي طريقة. استنسخ العينة، شغّلها ضد مستند مر بجولة مراجعة حقيقية، واقرأ أعداد المتأثرين. عادةً ما تكون أعلى مما تتوقع، وهذا هو الهدف كله.

موارد إضافية