مشروع مصغر: سكربت يقرأ مجلداً كاملاً ويصنف الملفات حسب امتدادها


مشروع مصغر: سكربت يقرأ مجلداً كاملاً ويصنف الملفات حسب امتدادها

مشروع مصغر: سكربت يقرأ مجلداً كاملاً ويصنف الملفات حسب امتدادها

مرحباً أيها المطورون! اليوم، سنقوم ببناء سكربت Node.js بسيط ولكنه قوي، يقرأ محتويات مجلد معين، ثم يصنف الملفات الموجودة فيه بناءً على امتداداتها.

هذا المشروع سيعلمنا كيفية التفاعل مع نظام الملفات (File System) في Node.js وكيفية معالجة المسارات.

ملاحظة تقنية: سنستخدم وحدة fs.promises لمعالجة العمليات بشكل غير متزامن، مما يضمن أداءً أفضل لتطبيقاتنا.

الخطوة 1: تهيئة المشروع وقراءة محتويات المجلد

في هذه الخطوة، سنقوم بإعداد مشروع Node.js الخاص بنا واستيراد الوحدات الضرورية. سنستخدم وحدة fs للوصول إلى نظام الملفات ووحدة path لمعالجة مسارات الملفات. ثم سنقوم بقراءة جميع العناصر (ملفات ومجلدات) داخل المجلد المستهدف.

const fs = require('fs').promises; // استيراد وحدة نظام الملفات (fs) بصيغة الوعود (Promises) لعمليات غير متزامنة
const path = require('path');     // استيراد وحدة المسار (path) للتعامل مع مسارات الملفات والمجلدات

// تحديد المجلد المستهدف. يمكن تغيير هذا المسار إلى أي مجلد ترغب في فحصه.
const targetDirectory = './test_files'; // تأكد من وجود هذا المجلد في نفس مستوى السكربت أو قم بتغيير المسار

async function categorizeFiles() {
    console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">بدء تصنيف الملفات في المجلد: ${targetDirectory}</code>);
    try {
        // قراءة جميع العناصر (ملفات ومجلدات) داخل المجلد المستهدف
        const items = await fs.readdir(targetDirectory); 
        console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">تم العثور على ${items.length} عنصرًا.</code>);

        // هنا سنكمل باقي المنطق في الخطوات التالية

    } catch (error) {
        console.error(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">حدث خطأ أثناء قراءة المجلد: ${error.message}</code>);
    }
}

// استدعاء الدالة لبدء التنفيذ
categorizeFiles();

الخطوة 2: تحديد الملفات واستخراج الامتدادات

بعد قراءة محتويات المجلد، نحتاج إلى التمييز بين الملفات والمجلدات الفرعية. سنستخدم دالة fs.stat() للحصول على معلومات حول كل عنصر، ثم نتحقق مما إذا كان ملفاً باستخدام isFile(). بمجرد تأكيد أنه ملف، سنستخرج امتداده باستخدام path.extname().

const fs = require('fs').promises;
const path = require('path');

const targetDirectory = './test_files';

async function categorizeFiles() {
    console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">بدء تصنيف الملفات في المجلد: ${targetDirectory}</code>);
    try {
        const items = await fs.readdir(targetDirectory);
        console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">تم العثور على ${items.length} عنصرًا.</code>);

        const fileCategories = {}; // كائن لتخزين الملفات المصنفة حسب امتدادها

        for (const item of items) {
            const itemPath = path.join(targetDirectory, item); // بناء المسار الكامل للعنصر
            try {
                const stats = await fs.stat(itemPath); // الحصول على معلومات حول العنصر

                if (stats.isFile()) { // التحقق مما إذا كان العنصر ملفاً
                    const ext = path.extname(item).toLowerCase(); // استخراج امتداد الملف وتحويله لأحرف صغيرة
                    const extension = ext || 'no_extension'; // إذا لم يكن هناك امتداد، نعتبره 'no_extension'

                    // هنا سنضيف الملف إلى التصنيف المناسب في الخطوة التالية
                    console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">- تم العثور على ملف: ${item}, الامتداد: ${extension}</code>);

                } else if (stats.isDirectory()) {
                    console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">- تم تخطي المجلد: ${item}</code>);
                }
            } catch (statError) {
                console.warn(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">تحذير: لا يمكن الوصول إلى ${itemPath} - ${statError.message}</code>);
            }
        }

    } catch (error) {
        console.error(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">حدث خطأ أثناء قراءة المجلد: ${error.message}</code>);
    }
}

categorizeFiles();

الخطوة 3: بناء هيكل التصنيف وطباعة النتائج

الآن بعد أن أصبح بإمكاننا تحديد الملفات وامتداداتها، سنقوم بإنشاء كائن fileCategories لتخزين الملفات. كل مفتاح في هذا الكائن سيكون امتداداً (مثل .txt أو .jpg)، وقيمته ستكون مصفوفة تحتوي على أسماء الملفات التي تحمل هذا الامتداد. أخيراً، سنقوم بطباعة النتائج النهائية بشكل منظم.

const fs = require('fs').promises;
const path = require('path');

const targetDirectory = './test_files';

async function categorizeFiles() {
    console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">بدء تصنيف الملفات في المجلد: ${targetDirectory}</code>);
    try {
        const items = await fs.readdir(targetDirectory);
        console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">تم العثور على ${items.length} عنصرًا.</code>);

        const fileCategories = {}; // كائن لتخزين الملفات المصنفة حسب امتدادها

        for (const item of items) {
            const itemPath = path.join(targetDirectory, item);
            try {
                const stats = await fs.stat(itemPath);

                if (stats.isFile()) {
                    const ext = path.extname(item).toLowerCase();
                    const extension = ext || 'no_extension';

                    // إذا لم يكن الامتداد موجوداً كمفتاح في الكائن، نقوم بإنشاء مصفوفة فارغة له
                    if (!fileCategories[extension]) {
                        fileCategories[extension] = [];
                    }
                    // إضافة اسم الملف إلى المصفوفة الخاصة بامتداده
                    fileCategories[extension].push(item);

                } else if (stats.isDirectory()) {
                    // لا نفعل شيئاً مع المجلدات الفرعية في هذا السكربت، يمكن توسيعه لاحقاً.
                }
            } catch (statError) {
                console.warn(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">تحذير: لا يمكن الوصول إلى ${itemPath} - ${statError.message}</code>);
            }
        }

        console.log('\n--- نتائج التصنيف ---');
        // التحقق مما إذا كان هناك أي ملفات تم تصنيفها
        if (Object.keys(fileCategories).length === 0) {
            console.log('لم يتم العثور على أي ملفات قابلة للتصنيف.');
        } else {
            // طباعة النتائج النهائية بشكل منظم
            for (const ext in fileCategories) {
                console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">
امتداد: ${ext || '[بدون امتداد]'}</code>);
                fileCategories[ext].forEach(file => console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">  - ${file}</code>));
            }
        }

    } catch (error) {
        console.error(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">حدث خطأ أثناء قراءة المجلد: ${error.message}</code>);
    }
}

categorizeFiles();
ملاحظة: قبل تشغيل السكربت، تأكد من إنشاء مجلد باسم test_files في نفس مكان السكربت ووضع بعض الملفات فيه (مثل document.txt, image.jpg, report.pdf, archive.zip).

الكود النهائي الكامل

إليكم الكود النهائي الكامل الذي يجمع جميع الخطوات المذكورة أعلاه. يمكنكم حفظه كملف categorizer.js وتشغيله باستخدام node categorizer.js.

const fs = require('fs').promises; // استيراد وحدة نظام الملفات (fs) بصيغة الوعود (Promises) لعمليات غير متزامنة
const path = require('path');     // استيراد وحدة المسار (path) للتعامل مع مسارات الملفات والمجلدات

// تحديد المجلد المستهدف. يمكن تغيير هذا المسار إلى أي مجلد ترغب في فحصه.
const targetDirectory = './test_files'; // تأكد من وجود هذا المجلد في نفس مستوى السكربت أو قم بتغيير المسار

async function categorizeFiles() {
    console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">بدء تصنيف الملفات في المجلد: ${targetDirectory}</code>);
    try {
        // قراءة جميع العناصر (ملفات ومجلدات) داخل المجلد المستهدف
        const items = await fs.readdir(targetDirectory); 
        console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">تم العثور على ${items.length} عنصرًا.</code>);

        const fileCategories = {}; // كائن لتخزين الملفات المصنفة حسب امتدادها

        for (const item of items) {
            const itemPath = path.join(targetDirectory, item); // بناء المسار الكامل للعنصر
            try {
                const stats = await fs.stat(itemPath); // الحصول على معلومات حول العنصر

                if (stats.isFile()) { // التحقق مما إذا كان العنصر ملفاً
                    const ext = path.extname(item).toLowerCase(); // استخراج امتداد الملف وتحويله لأحرف صغيرة
                    const extension = ext || 'no_extension'; // إذا لم يكن هناك امتداد، نعتبره 'no_extension'

                    // إذا لم يكن الامتداد موجوداً كمفتاح في الكائن، نقوم بإنشاء مصفوفة فارغة له
                    if (!fileCategories[extension]) {
                        fileCategories[extension] = [];
                    }
                    // إضافة اسم الملف إلى المصفوفة الخاصة بامتداده
                    fileCategories[extension].push(item);

                } else if (stats.isDirectory()) {
                    // لا نفعل شيئاً مع المجلدات الفرعية في هذا السكربت، يمكن توسيعه لاحقاً.
                    // console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">- تم تخطي المجلد: ${item}</code>);
                }
            } catch (statError) {
                console.warn(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">تحذير: لا يمكن الوصول إلى ${itemPath} - ${statError.message}</code>);
            }
        }

        console.log('\n--- نتائج التصنيف ---');
        // التحقق مما إذا كان هناك أي ملفات تم تصنيفها
        if (Object.keys(fileCategories).length === 0) {
            console.log('لم يتم العثور على أي ملفات قابلة للتصنيف.');
        } else {
            // طباعة النتائج النهائية بشكل منظم
            for (const ext in fileCategories) {
                console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">
امتداد: ${ext || '[بدون امتداد]'}</code>);
                fileCategories[ext].forEach(file => console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">  - ${file}</code>));
            }
        }

    } catch (error) {
        console.error(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">حدث خطأ أثناء قراءة المجلد: ${error.message}</code>);
    }
}

// استدعاء الدالة لبدء التنفيذ
categorizeFiles();

النتيجة المتوقعة

عند تشغيل السكربت بعد إنشاء مجلد test_files ووضع بعض الملفات فيه (مثل file1.txt، image.jpg، document.pdf، script.js، README)، ستكون النتيجة في الطرفية (Console) مشابهة لما يلي:

بدء تصنيف الملفات في المجلد: ./test_files
تم العثور على 5 عنصرًا.

--- نتائج التصنيف ---

امتداد: .txt
  - file1.txt

امتداد: .jpg
  - image.jpg

امتداد: .pdf
  - document.pdf

امتداد: .js
  - script.js

امتداد: no_extension
  - README

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