💡 مثال کامل قابل اجرا در GitHub موجود است:
office-metadata-pii-cleanup-nodejs

Introduction

یک نقطهٔ آپلود یک فایل DOCX را از یک کارمند می‌گیرد و آن را در ارتباط با یک تیکت مشتری ذخیره می‌کند. متن مشکلی ندارد. اما ویژگی‌ها (properties) مشکل دارند: نام شخصی که آن را نوشت، همکار که آخرین بار ذخیره کرده، مدیر بخش از قالب شرکتی، و چون از 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

پروژهٔ نمونه نسخهٔ ۲۶.۷ را قفل می‌کند و یک ورودی 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);

نکات کلیدی:

  • چهار برچسب گروه هویت را پوشش می‌دهند: سازنده (creator)، ویرایشگر (editor)، مدیر (manager) و فیلد شرکت (company) شرکتی.
  • Title، Subject و Keywords دست‌نخورده می‌مانند، بنابراین یک فهرست رکورد که بر این فیلدها کلید می‌زند همچنان کار می‌کند.
  • removeProperties به جای یک مقدار بولی، تعداد موارد تحت تأثیر را برمی‌گرداند.

کل کد را در یک try/finally بپیچید و در بخش finally metadata.close() را فراخوانی کنید. بایندینگ فایل را تا آن زمان باز نگه می‌دارد و یک حلقه بدون این کار به سرعت به محدودیت دستگیره‌ها می‌رسد.

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

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

Pre‑publication gate

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

Best Practices and Tips

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

Troubleshooting Common Issues

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

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

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

Conclusion

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

Additional Resources