💡 דוגמה מלאה עובדת זמינה ב‑GitHub:
scrub-office-document-pii-dotnet
הדרך הישנה הייתה כואבת
הנה איך השגרה נראתה. פותחים את המסמך, קובץ, מידע, בדיקת בעיות, בדיקת מסמך, מסמנים את התיבות, מסירים הכול, שומרים בשם חדש, סוגרים, פותחים את הבא. אחרי ארבעים קבצים מישהו שם לב ש‑Inspect Document גם הסיר את הכותרת שעליה מתבססים מפתחות האינדקס של הרשומות, וש‑העותק שנשלח לפני שעה עדיין נשא מזהה מאשר SharePoint, מכיוון שהקובץ נשמר מיישום אחר שכתב את השדה בחזרה.
הסרת PII ממטא‑דטה היא יכולת של GroupDocs.Metadata עבור .NET שמוחקת תכונות נושאות זהות ממסמכי Office באופן תכנותי ומדווחת מה נשאר אחרי כן. השגרה הידנית נכשלת בשלושה מישורים: היא לא מתרחבת מעבר למספר מצומצם של קבצים, היא “הכל או כלום” לגבי אילו שדות יוסרו, והיא לא מייצרת רישום של מה שהוסר. מאמר זה מציג את גרסת .NET של אותה עבודה, קבוצה של תכונות בכל פעם.
זה עוזר לדעת מה באמת נמצא שם. קובץ Word שעבר סבב ביקורת בדרך כלל מכיל Author ו‑LastSavedBy מחשבון Windows של מי ששמר אותו, Manager ו‑Company מתבנית החברה, מונה גרסאות, TotalEditingTime, חותמת זמן LastPrinted, ומונים של שרשראות תגובות. מוסיפים SharePoint לשרשרת ומקבלים גם מזהי מאשרים, נתיבי זרימת עבודה, URI של סוג תוכן, והתבנית שממנה נוצר המסמך. אף אחד מהדברים האלה אינו נראה על העמוד, והכל נוסע באותו קובץ.
יש דרך טובה יותר
הכל ב‑GroupDocs.Metadata עבור .NET רץ דרך מנוע חיפוש תכונות יחיד. RemoveProperties מקבל lambda על MetadataProperty, מוחק כל תכונה שה‑lambda מקבלת, ומחזיר את הספירה. FindProperties מריץ את אותה lambda ללא כתיבה. לתכונות יש גם תגים, ולכן Tags.Person.Creator מזהה שדות בסגנון מחבר במגוון פורמטים וחבילות במקום להתאים שמות מילוליים שונים לפי יישום יוצר.
זה נותן שלושה סוגי ניקוי במקום כפתור אחד: מעבר תגים לשדות זהות, מעברי שם למשפחות כמו תגובות וגרסאות, ו‑Sanitize() כשאין דבר שצריך לשרוד. שלושתם מחזירים מספרים, והמספרים הם מה שהופך את המעבר לבדיקה ניתנת לאימות.
הדרך החדשה: תנאי לכל קבוצת תכונות
שלב 1 – ניקוי השמות
ארבע בדיקות תגים מכסות את קבוצת הזהות. שדות תיאוריים נשארים ללא שינוי, וזה ההבדל מה‑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 היא המגן ששומר על אפס כנה: ללא זה, קובץ שלא ניתן לקרוא וקובץ נקי נראים זהים למזמין.
שלב 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 ולא API של מטא‑דטה.
למה לא פשוט לקרוא ל‑Sanitize על הכל?
כי רוב המסמכים עדיין בשימוש. Sanitize() מוחק כל חבילה שזוהתה, כולל Title, Subject, ו‑Keywords, השדות שמערכת רשומות ומנוע חיפוש מסתמכים עליהם. השתמשו במעברים הממוקדים בזמן שהקובץ מסתובב פנימית, שמרו על המטא‑דטה התיאורית, והזמינו מחיקה מלאה רק עבור העותק שעוזב את הארגון.
השוואה ציד‑ציד: לפני vs. אחרי
| בדיקה ידנית | GroupDocs.Metadata עבור .NET | |
|---|---|---|
| סלקטיביות | Remove All, כולל שדות תיאוריים | תנאי אחד לכל קבוצת תכונות |
| כיסוי | השדות שהדיאלוג מציג | כל חבילה שהספרייה מזהה, כולל חלקי OOXML מותאמים |
| רישום | אין | ספירת פריטים שהושפעו מוחזרת לכל פעולה |
| אימות | פתיחה מחדש ובדיקה | סריקת FindProperties עם רשימות מטא‑דטה ורמת‑תוכן |
| אצווה של 200 קבצים | 200 לחיצות | לולאה אחת, חמש פעולות, שורת לוג אחת לכל קובץ |
השורה שמשנה את ההתנהגות היא הרישום. ברגע שכל מעבר מחזיר ספירה, הניקוי מפסיק להיות שלב שמישהו זוכר לבצע והופך לנתון שהצינור יכול לאמת: סף בבדיקה, שדה בטבלת ביקורת, תנאי שמפסיק משימה לילה. זה גם השורה שהשגרה הידנית לא יכולה לייצר ברמת משמעת כלשהי.
דוגמה מהעולם האמיתי: הוק לפני שליחה
פורטל תמיכה מאפשר לצוות לצרף מסמכים לפניות לקוחות. מטפל הקבצים כעת מריץ את מעבר הזהות ואת מעבר השרת לפני שהקובץ נשמר, רושם את שני הספירות נגד הפנייה, ומבצע בדיקת דליפה על העותק השמור. רשימת דליפות מטא‑דטה שאינה ריקה דוחה את ההעלאה עם הודעה שמציינת את התכונה הפוגעת, כך שהאדם המצרף את הקובץ מגלה זאת מיד ולא אחרי שהקובץ הגיע ללקוח.
שני פרטים עושים את ההוק פרקטי. המעברים כותבים לנתיב חדש, כך שהמקור נשאר באחסון של העובד ואין שום דבר שנשבר בצעד אוטומטי. והספירות נכנסות לרשומת הפנייה לצד הקובץ המצורף, מה שאומר שהתשובה ל‑“מה הוסר מהמסמך הזה” היא מספר שמור ולא הנחה לגבי מה שהצינור בדרך כלל עושה.
בפעם הראשונה שהצבתי את הבדיקה על תבנית אמיתית, היא החזירה ערך Manager שהמעבר הזהות הסיר שניות לפני והתבנית הארגונית כתבה חזרה על שמירה. קריאת ההסרה עבדה בדיוק כפי שתועד; הבעיה הייתה בצינור סביבו, ורק הקריאה חזרה חשפה זאת.
מה עוד אפשר לעשות עם GroupDocs.Metadata?
מנוע התנאים קורא גם. השוואת תכונות בין שני גרסאות של מסמך חושפת שינויי בעלות ו‑re‑authoring מחוץ לתהליך הביקורת, וה‑metadata scrubbing overview מסביר היכן כלי אינטראקטיבי עדיין מתאים לצד מעבר מבוסס API. מכיוון שמערכת התגים חוצה פורמטים, תנאי הזהות שנכתב כאן פועל גם נגד PDFs, תמונות וקבצי שמע ללא שינוי.
הניידות הזו שווה תכנון מראש. כלל ניקוי שנכתב כ‑lambda על MetadataProperty הוא C# רגיל, ולכן ניתן לשים אותו בספרייה משותפת, לבדוק יחידות נגד מסמכי‑fixture, ולהפעיל אותו על‑ידי כל שירות שצריך זאת: נקודת קצה לייצוא, משימת רשומות מתוזמנת, או שלב בנייה שמנקה קבצי תיעוד לפני שחרור. הכללים נשארים במקום אחד; רק אתרי הקריאה משתנים.
סיכום
ארבעה מעברים ממוקדים, ניקוי מלא אחד, סריקת אימות אחת. סט זה מכסה את הטווח המעשי למסמכי Office: שמרו על המטא‑דטה התיאורית בזמן שהקובץ במעגל, נקה הכל כשזה יוצא, והוכיחו את התוצאה בכל מקרה. שכפלו את הדוגמה, הריצו אותה נגד מסמך שעבר סבב ביקורת אמיתי, וקראו את ספירות הפריטים שהושפעו. הן בדרך כלל גבוהות מהצפוי, וזהו המטרה.