💡 Full working example available on 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

این بسته از طریق Java برای Node.js است، بنابراین ماشین باید یک runtime جاوا در کنار Node داشته باشد.

Installation

npm install @groupdocs/groupdocs.metadata

پروژهٔ نمونه نسخهٔ ۲۶.۷ را قفل می‌کند و یک ورودی 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 بپیچید و در بخش finally metadata.close() را صدا بزنید. بایندینگ فایل را تا آن زمان باز نگه می‌دارد و یک حلقه بدون این کار به سرعت به حد دسترسی به handleها می‌رسد.

Step 2 - Select properties by name

رشته‌های نظرات، شمارنده‌های بازنگری و فیلدهای سرور هیچ برچسبی ندارند. برای این‌ها، WithNameSpecification(needle, false) هر ویژگی‌ای که نامش شامل needle باشد پیدا می‌کند و یک سازندهٔ چهار خطی یک زیررشته برای هر یک می‌سازد:

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

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

Pre‑publication gate

یک مرحلهٔ ساخت اسناد پیوست‌های مستندات را قبل از انتشار پاک‌سازی می‌کند، با استفاده از sanitize() چون هیچ‌یک از این فایل‌ها به حفظ متادیتای خود نیاز ندارند.

Best Practices and Tips

  • همیشه به مسیر جدیدی بنویسید تا نسخهٔ اصلی برای حل اختلافات باقی بماند.
  • شیء متادیتا را در یک بلوک finally ببندید، به‌ویژه در حلقه‌ها.
  • در کارهای دسته‌ای مشخصات را با .or() ترکیب کنید؛ یک باز‑کردن و یک ذخیره‌سازی بهتر از چهار بار است.
  • شمارش تحت تأثیر هر پاس را لاگ کنید، حتی صفرها را، تا فرمت‌های ناشناخته قابل مشاهده باشند.

Troubleshooting Common Issues

شمارش تحت تأثیر صفر است در سندی که می‌دانید کثیف است
اطمینان حاصل کنید که فرمت ورودی شناسایی شده است قبل از این‌که نتیجهٔ تمیز بودن فایل را استنتاج کنید؛ یک فایل غیرقابل خواندن و یک فایل تمیز هر دو صفر برمی‌گردانند.

بررسی نشت ویژگی‌هایی را گزارش می‌کند که تازه حذف کرده‌اید
اسکن را به مسیر خروجی ذخیره‌شده، نه مسیر ورودی، ارجاع دهید. اسکن هر فایلی که به آن داده می‌شود را می‌خواند.

بالون‌های نظرات هنوز در Word قابل مشاهده‌اند
متن نظرات در بدنهٔ سند lives می‌کند، نه در بستهٔ متادیتا. GroupDocs.Metadata ویژگی‌های مرتبط با نظرات را پاک می‌کند؛ حذف خود بالون‌ها نیاز به کتابخانهٔ ویرایش محتوا مانند Aspose.Words دارد.

Conclusion

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

Additional Resources