💡 דוגמה מלאה זמינה ב‑GitHub:
office-metadata-pii-cleanup-nodejs

Introduction

נקודת קצה להעלאה מקבלת קובץ DOCX מעובד ומאחסנת אותו בטיקיט של לקוח. הטקסט תקין. המאפיינים אינם: שם הקובץ כולל את האדם שכתב אותו, הקולגה ששמר אותו לאחרונה, מנהל המחלקה מתבנית החברה, ובגלל שהקובץ הגיע מ‑SharePoint, המאשר שחתם עליו.

מתקן מטא‑נתונים הוא סקריפט קטן שמוחק את המאפיינים האלה לפני שהקובץ נשמר ולאחר מכן בודק את העבודה שלו. מדריך זה בונה מתקן כזה ב‑Node.js עם GroupDocs.Metadata, בארבעה שלבים: בחירת מאפיינים לפי תג, בחירתם לפי שם, מחיקת הכול כאשר הסלקטיביות מפסיקה לעזור, ואימות מה נשאר. כל שלב הוא כמה שורות, והסקריפט המוגמר הוא מתחת למאה שורות.

Why Metadata Sanitization Matters

הנתונים מצטברים ללא בחירה של אף אחד. Word כותב Author ו‑LastSavedBy מחשבון מערכת ההפעלה בכל שמירה, שומר מונה גרסאות, עוקב אחרי TotalEditingTime, ורושם LastPrinted. שרתי מסמכים מוסיפים נתיבי זרימת עבודה, מזהי מאשרים, ו‑URI של סוגי תוכן בעת הצ’ק‑אין. שום דבר מהזה לא מופיע כשקוראים או מדפיסים את המסמך, ולכן עריכה חוזרת לעולם לא תתפוס זאת.

היתרון ב‑Node.js במקום לעשות זאת ידנית הוא שהסקריפט מחזיר מספרים: כל קריאת הסרה מדווחת כמה מאפיינים נמחקו, והספירה הזו ניתנת לכתיבה ללוג, לאימות בבדיקה, או לצירוף לרשומה שהמסמך שייך אליה.

קיים סיבה נוספת, פחות ברורה עד שמופעלת משימת אצווה. ניקוי ידני הוא החלטה שנעשית פעם אחת לכל קובץ על ידי מי שמטפל בו, ולכן שני אנשים שמנקים את אותו סוג מסמך יפיקו תוצאות שונות. סקריפט מתקן את הכלל במקום אחד: אותם ארבעה תגיות, אותן רשימות תתי‑מחרוזות, מיושמים באופן זהה בין אם התור מכיל שלושה קבצים או שלושה אלפים.

Prerequisites

החבילה היא Node.js דרך Java, ולכן המכונה צריכה סביבת ריצה של Java לצד Node.

Installation

npm install @groupdocs/groupdocs.metadata

פרויקט הדוגמה קובע גרסה 26.7 ומוסיף ערך overrides שמגדיר nan ל‑^2.22.0, מה שמאפשר לבנייה של הקישוריות הטבעית על גרסאות Node נוכחיות. ללא קובץ רישיון הספרייה פועלת במצב הערכה, וזה מספיק כדי לעקוב אחרי כל שלב כאן.

Step 1 - Select properties by what they mean

שמות המאפיינים שונים בין פורמטים וחבילות, ולכן הכלל הראשון תואם לפי תגיות במקום זאת. ContainsTagSpecification מקבלת תגית ומתאימה לכל מאפיין שנושא אותה; .or() ממזג ספציפיקציות לאחת.

const T = groupdocs.Tags;
const spec = new groupdocs.ContainsTagSpecification(T.getPerson().getCreator())
  .or(new groupdocs.ContainsTagSpecification(T.getPerson().getEditor()))
  .or(new groupdocs.ContainsTagSpecification(T.getPerson().getManager()))
  .or(new groupdocs.ContainsTagSpecification(T.getCorporate().getCompany()));
const affected = metadata.removeProperties(spec);
metadata.save(outputPath);

נקודות מפתח:

  • ארבע תגיות מכסות את קבוצת הזהות: יוצר, עורך, מנהל, ושדה החברה של החברה.
  • Title, Subject, ו‑Keywords נשארים ללא שינוי, כך שמאגר רישומים שמפתח עליהם ממשיך לעבוד.
  • removeProperties מחזיר את מספר הפריטים שהושפעו במקום ערך בוליאני.

עטפו את כל הקוד ב‑try/finally עם metadata.close() ב‑finally. הקישור מחזיק את הקובץ פתוח עד אז, ולולאה ללא סגירה תיגמר ממספר המופעים.

Step 2 - Select properties by name

שרשורי תגיות, מוני גרסאות ושדות שרת אינם נושאים תג. עבורם, WithNameSpecification(needle, false) מתאים לכל מאפיין ששמו מכיל את המחרוזת, ובונה של ארבע שורות מחבר אחד לכל תת‑מחרוזת:

let spec = null;
for (const needle of needles) {
  const s = new groupdocs.WithNameSpecification(needle, false /* fullMatch */);
  spec = spec ? spec.or(s) : s;
}
return spec;

שלושה מעברים משתמשים בבונה הזה עם רשימות שונות. תחילה עוברים על תגובות:

const affected = metadata.removeProperties(
  nameContainsSpec(['Comment', 'Reviewer', 'Reviewed']));
metadata.save(outputPath);

ציר הזמן של העריכה הוא הקבוצה שנוטה להישכח, והיא מתארת איך נוצר המסמך:

const affected = metadata.removeProperties(nameContainsSpec([
  'Revision', 'TrackedChange', 'LastPrinted', 'TotalEditingTime', 'EditTime',
]));
metadata.save(outputPath);

מעבר SharePoint הוא אותו קריאה עם Server, Workflow, Approver, ContentType, ו‑Template. התאמת תתי‑מחרוזת היא מכוונת: היא תופסת CommentsCount יחד עם Comment מבלי לשמור רשימת שמות מדויקת לכל פורמט.

Step 3 - Wipe everything when selectivity stops helping

לגרסה שמיועדת לצאת מהארגון, קריאה אחת מחליפה את ארבעת המעברים:

const affected = metadata.sanitize();
metadata.save(outputPath);

sanitize() מוחק כל חבילת מטא‑נתונים שהספרייה מזהה, כולל חלקי OOXML מותאמים, והספירה שלו בדרך כלל עולה על סכום המעברים הממוקדים. היא גם לוקחת את Title ו‑Subject, ולכן היא מתאימה לשימוש בגבול ולא בתוך לולאת ביקורת.

Step 4 - Verify, because a silent miss looks like success

הסריקה משתמשת באותן ספציפיקציות דרך findProperties, שקוראת ללא כתיבה. התוצאה היא אוסף Java, ולכן היא נבדקת לפי אינדקס:

const props = metadata.findProperties(tagSpec.or(nameSpec));
for (let i = 0; i < props.getCount(); i++) {
  const p = props.get_Item(i);
  const val = p.getValue && p.getValue();
  const value = val ? String(val.getRawValue ? val.getRawValue() : val) : '';
  if (!value || value === '0' || value === '0.0') continue;
  leaks.push(`${p.getName()}=${value}`);
}

הפילטר של ערכים ריקים ואפסים מרוויח את מקומו. הוספתי אותו אחרי ריצה שנכשלה על מונה גרסאות שנוקה ל‑0, והסריקה דיווחה במדויק על המאפיין שנשאר.

Complete Working Example

המאגרים מחברים את שש הפונקציות ל‑index.js, שמיישם את הרישיון, מריץ כל מעבר על resources/pii-sample.docx, מאמת שכל קובץ פלט קיים, ומסיים באימות שרשימת הדליפות ריקה. כשל באימות גורם ליציאה עם קוד חזרה שונה מאפס, ולכן כל התהליך פועל כביקורת ב‑CI ולא כהדגמה לקריאה בלבד.

פרט אחד שכדאי להעתיק לגרסה שלכם: כל מעבר קורא את אותו קובץ מקור וכותב קובץ פלט נפרד, במקום לשרשר קובץ מנוקה לקובץ הבא. כך ספירות ההשפעה נשארות עצמאיות, וקו לוג של מעבר התגובות מדווח מה מצא כלל התגובות ולא מה נשאר אחרי שהכלל הזה רץ.

When should I run a targeted pass instead of sanitize()?

בכל פעם שהמסמך עדיין נמצא בשימוש. קבצים שמסתובבים בין מבקרים מסתמכים על Title, Subject, ו‑Keywords לחיפוש וסיווג, ו‑sanitize() מסיר את שלושתם יחד עם הנתונים האישיים. הריצו את מעברי הזהות והתגובות במהלך שיתוף הפעולה, שמרו על השדות המתארים, והשתמשו במחיקה מלאה רק עבור העותק שעוזב את הארגון.

Real-World Applications

Upload handler

נתיב Express מנקה קובץ מצורף לפני כתיבתו לאחסון, רושם את ספירות ההשפעה בטיקיט, ודוחה את ההעלאה כאשר רשימת הדליפות אינה ריקה.

Nightly export job

עובד עובר על תיקיית ייצוא, מפעיל את מעברי הזהות והשרת, ומפסיק את המשימה במקום לרשום אזהרה כאשר מסמך עדיין מדווח על PII שארית.

Pre‑publication gate

שלב בנייה מנקה קבצי תיעוד לפני שחרור, משתמש ב‑sanitize() מכיוון שאין צורך לשמר את המטא‑נתונים בקבצים אלה.

Best Practices and Tips

  • תמיד כתבו לנתיב חדש כך שהמקור יישאר זמין למטרות מחלוקת.
  • סגרו את אובייקט המטא‑נתונים ב‑finally, במיוחד בלולאות.
  • מיזוג ספציפיקציות עם .or() בעבודות אצווה; פתיחה אחת ושמירה אחת עדיפות על ארבע.
  • רשמו את ספירת ההשפעה לכל מעבר, כולל אפסים, כדי שמבנה פורמט לא מוכר יהיה גלוי.

Troubleshooting Common Issues

ספירת ההשפעה היא אפס על מסמך שאתה יודע שהוא מזוהם
ודאו שהפורמט של הקלט מזוהה לפני שמסיקים שהקובץ נקי; קובץ שלא ניתן לקרוא וקובץ נקי מניבים אפס זהה.

בדיקת הדליפה מדווחת על מאפיינים שהסרת זה עתה
הפנו אותה לנתיב הפלט השמור, ולא לנתיב הקלט. הסריקה קוראת את הקובץ שהיא מקבלת.

בלוני ההערות עדיין נראים ב‑Word
טקסט ההערה נמצא בגוף המסמך, לא בחבילת מטא‑נתונים. GroupDocs.Metadata מנקה את המאפיינים הקשורים להערות; להסרת הבלונים עצמם נדרשת ספרייה לעריכת תוכן כגון Aspose.Words.

Conclusion

ארבעה שלבים, שש פונקציות, סקריפט אחד שמדווח מה הוא עשה. ספציפיקציות תגיות מטפלות בקבוצת הזהות בין פורמטים, ספציפיקציות שם מכסות את המשפחות שהתגיות אינן מסווגות, sanitize() מטפל בגבול, וסריקת הדליפות הופכת את הכול לביקורת. שיבטו את המאגרים, הריצו אותו על מסמך שעבר סבב ביקורת אמיתי, והסתכלו על הספירות לפני שתחליטו אילו מעברים נדרשים בצינור שלכם.

Additional Resources