
هناك حصاد رائع جدًا لإدارة المشاريع المشتركة ، وترخيص LDAP ، ومزامنة الملفات مع الإصدار وشيء مثل برنامج مراسلة الشركات مع مؤتمرات الفيديو ، والتي تم إفسادها في أحدث الإصدارات. نعم ، أنا أتحدث عن Nextcloud. من ناحية أخرى ، أنا مؤيد لطريقة Unix وتقسيم التطبيقات بشكل واضح إلى وظائف منفصلة. من ناحية أخرى ، هذا المنتج أكثر من مستقر ، فهو يعمل لسنوات عديدة في عدة مشاريع دون أي مشاكل والصفارات الإضافية لا تتداخل بشكل خاص مع عمله. إذا كنت تريد ذلك حقًا ، يمكنك إلغاء أي لعبة تقريبًا هناك. المجتمع حيوي ويكمل بشكل كامل العديد من المكونات الإضافية المتوفرة كتطبيقات منفصلة.
اليوم سنقوم بنشره. لن أعطي تعليمات كاملة خطوة بخطوة ، لكنني سأحاول أن أذكر النقاط الرئيسية للهندسة المعمارية التي تستحق الاهتمام بها. على وجه الخصوص ، سنقوم بتحليل موازنة التحميل ونسخ قاعدة البيانات والصيانة الروتينية دون انقطاع الخدمة.
سنقوم بنشره في إصدار آمن من الفشل لشركة صغيرة تضم 150-1000 مستخدم ، ولكنه سيكون مفيدًا أيضًا للمستخدمين المنزليين.
ماذا تحتاج الشركات؟
الاختلاف الرئيسي بين الخدمة على خادم منزلي مريح مصنوع من الجوز والمطابقات من قطاع الشركة هو مسؤولية المستخدمين. ومع ذلك ، حتى في التثبيت المنزلي الخاص بي ، أعتبر أنه طريقة جيدة لإرسال رسائل إلى المستخدمين مع تحذير بشأن العمل المخطط له أو وقوع حادث محتمل. بعد كل شيء ، في ليلة السبت قد يقرر صديقك فجأة التعامل مع البيانات التي يستضيفها معك.
في حالة الشركة ، حتى لو كانت صغيرة ، فإن أي خدمة مهمة بسيطة تعني خسائر ومشاكل محتملة. خاصة إذا كان هناك العديد من العمليات المرتبطة بالخدمة.
على وجه الخصوص ، من واقع خبرتي ، فإن Nextcloud مطلوب للعديد من الميزات بين الشركات الصغيرة:
- توفير الوصول إلى الدلائل المشتركة والمزامنة.
- ميزة قاتلة مع توفير وصول خارجي داخل الاتحاد. يمكنك التكامل مع منتج مشابه من الزملاء وشركة أخرى.
- توفير وصول خارجي عبر رابط مباشر. من المفيد كثيرًا ، على سبيل المثال ، العمل في صناعة الطباعة وتحتاج إلى تبادل كميات كبيرة من البيانات الثقيلة مع العملاء.
- محرر مستندات Collabora يعمل على جانب الخادم ويعمل كواجهة أمامية لـ LibreOffice.
- الدردشات ومكالمات الفيديو. ميزة مثيرة للجدل قليلاً وليست مستقرة تمامًا ، ولكنها موجودة وتعمل. في الإصدار الأخير ، تم تثبيته بالفعل.
نحن نبني العمارة
لسوء الحظ ، في أحدث الإصدارات ، لا تتوفر وثائق تنفيذ مؤسسة Nextcloud إلا لأصحاب الاشتراكات المدفوعة. ومع ذلك ، كمرجع ، يمكنك أن تأخذ كتيبات أقدم لا تزال في المجال العام.

نموذجي للاستخدام المنزلي والتركيبات الفردية.
خيار الكل في واحد ليس سيئًا طالما لديك عدد قليل من المستخدمين ويمكنك تحمل وقت التوقف عن الصيانة الروتينية. على سبيل المثال ، أثناء التحديث. أيضًا ، يواجه المخطط المتجانس مع وضع على عقدة واحدة مشاكل في القياس. لذلك ، سنحاول الخيار الثاني.

يوصى بخيار النشر القابل للتوسع لأحمال العمل الأكبر.
المكونات الرئيسية للنظام:
- 1 موازن. يمكنك استخدام HAproxy أو Nginx. سأفكر في الخيار مع Nginx.
- 2-4 قطع خادم التطبيق (خادم الويب). تثبيت Nextcloud نفسه مع الكود الرئيسي في php.
- 2 ديسيبل. في التكوين القياسي الموصى به ، هذا هو MariaDB.
- تخزين NFS.
- Redis لتخزين استعلامات قاعدة البيانات مؤقتًا
موازن
مع هذه البنية ، سيكون لديك نقاط فشل أقل. نقطة الفشل الرئيسية هي موازن التحميل. إذا كان غير متوفر ، فلن يتمكن المستخدمون من الوصول إلى الخدمة. لحسن الحظ ، فإن تكوينه لـ nginx نفسه بسيط للغاية ، كما سننظر فيه أكثر ، وهو يحمل العبء دون أي مشاكل. يتم حل معظم حالات الفشل في الموازن عن طريق إعادة تشغيل البرنامج الخفي أو العقدة بأكملها أو النشر من نسخة احتياطية. لن يكون من غير الضروري أن يكون لديك احتياطي بارد مهيأ في موقع آخر مع تحويل حركة المرور يدويًا إليه في DNS.
يرجى ملاحظة أن الموازن هو أيضًا نقطة إنهاء SSL / TLS لعملائك ، ويمكن أن يمر الاتصال بالخلفية إما عبر HTTP للشبكات الداخلية الموثوقة ، أو باستخدام HTTPS إضافي إذا كانت حركة المرور إلى خادم التطبيق تمر عبر قنوات عامة غير موثوق بها.
قاعدة البيانات
الحل النموذجي هو MySQL / MariaDB في التنفيذ العنقودي في النسخ المتماثل الرئيسي والعبد. في نفس الوقت ، لديك قاعدة بيانات نشطة واحدة فقط ، والثانية تعمل في وضع الاستعداد السريع في حالة حدوث فشل طارئ للقاعدة الرئيسية أو أثناء العمل المجدول. يمكن أيضًا مراعاة موازنة الحمل ، لكنها أكثر صعوبة من الناحية الفنية. عند استخدام MariaDB Galera Cluster مع خيار النسخ المتماثل الرئيسي ، تحتاج إلى استخدام عدد فردي من العقد ، ولكن على الأقل ثلاثة. وبالتالي ، يتم تقليل مخاطر حالات انقسام الدماغ إلى الحد الأدنى ، عند قطع الاتصال بين العقد.
تخزين
أي حل أفضل لك يوفره بروتوكول NFS. للأحمال العالية ، ضع في اعتبارك IBM Elastic Storage أو Ceph. من الممكن أيضًا استخدام تخزين كائن متوافق مع S3 ، ولكن هذا خيار للتثبيتات الكبيرة جدًا.
HDD أو SSD
من حيث المبدأ ، بالنسبة للتركيبات متوسطة الحجم ، يكفي استخدام محرك الأقراص الثابتة فقط. سيكون الاختناق هنا هو iops عند القراءة من قاعدة البيانات ، مما يؤثر بشكل كبير على استجابة النظام ، ولكن إذا كان لديك Redis ، الذي يخزن كل شيء في ذاكرة الوصول العشوائي ، فلن تكون هذه مشكلة كبيرة. أيضًا ، سيتم تخزين جزء من ذاكرة التخزين المؤقت في memcached على خوادم التطبيق. ومع ذلك ، أوصي باستضافة خوادم التطبيقات الخاصة بك على SSD كلما أمكن ذلك. تبدو واجهة الويب أكثر استجابة. في هذه الحالة ، ستعمل مزامنة الملفات نفسها على عملاء سطح المكتب بنفس الطريقة تقريبًا المستخدمة عند استخدام محرك الأقراص الثابتة لهذه العقد.
سيتم تحديد سرعة المزامنة وتحميل الملفات من خلال أداء تخزين NFS الخاص بك.
تكوين الموازن
كمثال ، سأقدم طريقة بسيطة في التكوين الأساسي و nginx الفعال. نعم ، تتوفر العديد من كعكات تجاوز الفشل الإضافية في الإصدار المدفوع فقط ، ولكن حتى في الإصدار الأساسي ، فإنها تفي بمهمتها تمامًا. يرجى ملاحظة أن الموازنة المستديرة أو الموازنة العشوائية ليست مناسبة لنا ، لأن خوادم التطبيقات تخزن ذاكرة التخزين المؤقت لعملاء محددين.
لحسن الحظ ، تم حل هذا باستخدام طريقة ip_hash . في هذه الحالة ، سيتم تعيين جلسات المستخدم إلى خلفية محددة ، والتي سيتم توجيه جميع الطلبات الواردة من المستخدم إليها. هذه النقطة موصوفة في الوثائق:
, IP- . IPv4- IPv6- . , . , . .
لسوء الحظ ، عند استخدام هذه الطريقة ، يمكن أن تكون هناك مشاكل مع المستخدمين الذين يقفون وراء عنوان IP ديناميكي ويغيرونه باستمرار. على سبيل المثال ، على العملاء الذين يستخدمون الإنترنت عبر الهاتف المحمول ، والتي يمكن طرحها على طول مسارات مختلفة عند التبديل بين الخلايا. يتوفر ملف تعريف الارتباط اللاصق الذي يحل هذه المشكلة فقط في النسخة المدفوعة.
يصف ملف التكوين nginx هذا على النحو التالي:
upstream backend {
ip_hash;
server backend1_nextcloud.example.com;
server backend2_nextcloud.example.com;
server backend3_nextcloud.example.com;
server backend4_nextcloud.example.com;
}
في هذه الحالة ، سيتم توزيع الحمل بالتساوي قدر الإمكان بين خوادم التطبيق ، على الرغم من أنه بسبب ارتباط العميل بجلسة معينة ، قد تحدث اختلالات في التحميل. بالنسبة للتركيبات الصغيرة والمتوسطة الحجم ، يمكن إهمال ذلك. إذا كانت خلفياتك الخلفية مختلفة في القوة ، فيمكنك ضبط وزن كل منها. ثم سيحاول الموازن توزيع الحمل بالتناسب مع الأوزان المعطاة:
upstream backend {
ip_hash;
server backend1_nextcloud.example.com weight=3;
server backend2_nextcloud.example.com;
server backend3_nextcloud.example.com;
}
في المثال المعطى ، من بين 5 طلبات مستلمة ، 3 ستنتقل إلى الواجهة الخلفية 1 ، و 1 إلى الواجهة الخلفية 2 و 1 إلى الواجهة الخلفية 3.
إذا فشل أحد خوادم التطبيق ، سيحاول nginx إعادة توجيه الطلب إلى الخادم التالي من قائمة الخلفيات.
تكوين قاعدة البيانات
يمكن العثور على تفاصيل تكوين Master-Slave في الوثائق الرئيسية .
دعنا نلقي نظرة على بعض النقاط الرئيسية. أولاً ، نقوم بإنشاء مستخدم لنسخ البيانات:
create user 'replicant'@'%' identified by 'replicant_password';
grant replication slave on *.* to replicant;
flush privileges;
ثم نقوم بتحرير التكوين الرئيسي:
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
في منطقة كتلة "التسجيل والنسخ المتماثل" ، قم بإجراء التعديلات اللازمة:
[mysqld]
log-bin = /var/log/mysql/master-bin
log-bin-index = /var/log/mysql/master-bin.index
binlog_format = mixed
server-id = 01
replicate-do-db = nextcloud
bind-address = 192.168.0.6
على Slave ، نقوم بتكوين التكوين:
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
في منطقة كتلة "التسجيل والنسخ المتماثل" ، قم بإجراء التعديلات اللازمة:
[mysqld]
server-id = 02
relay-log-index = /var/log/mysql/slave-relay-bin.index
relay-log = /var/log/mysql/slave-relay-bin
replicate-do-db = nextcloud
read-only = 1
bind-address = 192.168.0.7
أعد تشغيل كلا الخادمين:
sudo systemctl restart mariadb
بعد ذلك ، ستحتاج إلى نسخ قاعدة البيانات إلى Slave.
على الماستر ، نقوم أولاً بإغلاق الجدول:
flush tables with read lock;
ثم ننظر إلى الحالة:
MariaDB [(none)]> show master status;
+-------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-------------------+----------+--------------+------------------+
| master-bin.000001 | 772 | | |
+-------------------+----------+--------------+------------------+
1 row in set (0.000 sec)
لا تخرج من وحدة تحكم قاعدة البيانات ، وإلا ستتم إزالة الأقفال!
سنحتاج إلى master_log_file و master_log_pos من هنا لتكوين Slave.
إغراق وإزالة الأقفال:
sudo mysqldump -u root nextcloud > nextcloud.sql
> unlock tables;
> exit;
ثم نقوم باستيراد التفريغ إلى Slave وإعادة تشغيل البرنامج الخفي:
sudo mysqldump -u root nextcloud < nextcloud.sql
sudo systemctl restart mariadb
بعد ذلك ، قم بإعداد النسخ المتماثل في وحدة التحكم:
MariaDB [(none)]> change master 'master01' to
master_host='192.168.0.6',
master_user='replicant',
master_password='replicant_password',
master_port=3306,
master_log_file='master-bin.000001',
master_log_pos=772,
master_connect_retry=10,
master_use_gtid=slave_pos;
نطلق ونتحقق من:
> start slave 'master01';
show slave 'master01' status\G;
يجب ألا يكون هناك أخطاء في الإجابة وستشير نقطتان إلى نجاح الإجراء:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
نشر عقد التطبيق
هناك عدة خيارات للنشر:
- يفرقع، ينفجر
- عامل ميناء الصورة
- تحديث يدوي
Snap متاح بشكل أساسي لـ Ubuntu. إنه جيد جدًا في تقديم تطبيقات احتكارية معقدة ، ولكن بشكل افتراضي. لكن لديها ميزة مزعجة إلى حد ما في بيئة صناعية - فهي تقوم تلقائيًا بتحديث حزمها عدة مرات في اليوم. سيتعين عليك أيضًا الاطلاع على عمليات الوصول الإضافية إلى الخارج إذا كان لديك شبكة داخلية محددة بشكل صارم. في الوقت نفسه ، فإن عكس مستودعاتها في الداخل ليس تافهًا تمامًا.
نعم ، هناك قنوات اشتراك ، وإصدارات كبرى ، نظريًا ، لا ينبغي تبديلها ، بل التفكير فيها. أوصي بالتحكم الكامل في عملية التحديث ، خاصةً لأنها غالبًا ما تكون مصحوبة بتغيير في بنية البيانات في قاعدة البيانات.
يعد Docker-image خيارًا جيدًا ، خاصةً إذا كانت البنية الأساسية الخاصة بك تعمل بالفعل على Kubernetes. من المحتمل أن تنتقل عقدة Redis نفسها إلى الكتلة بعد خوادم التطبيق.
إذا لم يكن لديك البنية التحتية للقيام بذلك ، فإن التحديث والنشر يدويًا من tar.gz يعد أمرًا مريحًا للغاية ويمكن التحكم فيه.
تذكر أنك ستحتاج إلى تثبيت خادم ويب على خادم التطبيق للتعامل مع الطلبات الواردة. أوصي بحزمة من nginx + php-fpm7.4. مع أحدث إصدارات php-fmp ، تحسن الأداء والاستجابة بشكل ملحوظ.
تكوين SSL / TLS
يجب عليك بالتأكيد الاعتماد على TLS 1.3 إذا كنت تقوم بتثبيت جديد ولا توجد مشاكل مع حزم nginx التي تعتمد على حداثة نظام opensl. على وجه الخصوص ، تسمح ميزة 0-RTT وغيرها من الأشياء الجيدة في بعض الأحيان بتسريع إعادة اتصال العميل بشكل كبير بسبب التخزين المؤقت. الأمان أيضًا أعلى بسبب الاستغناء عن البروتوكولات القديمة.
سأقدم التكوين الفعلي لخادم تطبيق nginx ، الذي يتواصل مع الموازن عبر TLS:
تكوين Nginx
upstream php-handler {
server unix:/var/run/php/php7.4-fpm.sock;
}
server {
listen 80;
server_name backend1_nextcloud.example.com;
# enforce https
root /var/www/nextcloud/;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
ssl_early_data on;
# listen [::]:443 ssl http2;
server_name backend1_nextcloud.example.com;
# Path to the root of your installation
root /var/www/nextcloud/;
# Log path
access_log /var/log/nginx/nextcloud.nginx-access.log;
error_log /var/log/nginx/nextcloud.nginx-error.log;
### SSL CONFIGURATION ###
ssl_certificate /etc/letsencrypt/live/backend1_nextcloud.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/backend1_nextcloud.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/backend1_nextcloud.example.com/fullchain.pem;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
#ssl_ciphers "EECDH+AESGCM:EECDH+CHACHA20:EECDH+AES256:!AES128";
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POL>
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 5m;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.4.4 8.8.8.8;
add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains; preload' always;
### SSL ###
# Add headers to serve security related headers
# Before enabling Strict-Transport-Security headers please read into this
# topic first.
# add_header Strict-Transport-Security "max-age=15768000;
# includeSubDomains; preload;";
#
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# The following 2 rules are only needed for the user_webfinger app.
# Uncomment it if you're planning to use this app.
#rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
#rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json
# last;
location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}
# set max upload size
client_max_body_size 512M;
fastcgi_buffers 64 4K;
# Enable gzip but do not remove ETag headers
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fon>
# Uncomment if your server is build with the ngx_pagespeed module
# This module is currently not supported.
#pagespeed off;
location / {
rewrite ^ /index.php;
}
location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
deny all;
}
location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param HTTPS on;
# Avoid sending the security headers twice
fastcgi_param modHeadersAvailable true;
# Enable pretty urls
fastcgi_param front_controller_active true;
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
}
location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}
# Adding the cache control header for js, css and map files
# Make sure it is BELOW the PHP block
location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
try_files $uri /index.php$request_uri;
add_header Cache-Control "public, max-age=15778463";
# Add headers to serve security related headers (It is intended to
# have those duplicated to the ones above)
# Before enabling Strict-Transport-Security headers please read into
# this topic first.
#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
#
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
# Optional: Don't log access to assets
access_log off;
}
location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ {
try_files $uri /index.php$request_uri;
# Optional: Don't log access to other assets
access_log off;
}
}صيانة روتينية
تذكر ، في بيئة صناعية ، تحتاج إلى توفير حد أدنى من التعطل وعدم حدوث أي توقف للترقيات أو حتى المزيد من النسخ الاحتياطية. تتمثل الصعوبة الرئيسية هنا في اعتماد حالة البيانات الوصفية في قاعدة البيانات والملفات نفسها ، والتي تتوفر من خلال NFS أو تخزين الكائنات.
عند ترقية خوادم التطبيق إلى إصدار ثانوي جديد ، لا توجد مشاكل خاصة. لكن الكتلة لا تزال بحاجة إلى التبديل إلى وضع الصيانة لتحديث بنية قاعدة البيانات.
قم بإيقاف تشغيل الموازن في وقت أقل تحميل واستمر في التحديث.
بعد ذلك نقوم بتنفيذ عملية التحديث اليدوي عليها من الملف tar.gz الذي تم تنزيله ، مع حفظ ملف التكوين config.php. التحديث عبر الويب في عمليات التثبيت الكبيرة فكرة سيئة للغاية!
نقوم بالتحديث عبر سطر الأوامر:
sudo -u www-data php /var/www/nextcloud/occ upgrade
بعد ذلك ، قم بتشغيل الموازن وأرسل حركة المرور إلى الخادم المحدث. للقيام بذلك ، نقوم بإزالة جميع خوادم التطبيقات غير المحدثة من الموازنة:
upstream backend {
ip_hash;
server backend1_nextcloud.example.com;
server backend2_nextcloud.example.com down;
server backend3_nextcloud.example.com down;
server backend4_nextcloud.example.com down;
}
يتم تحديث باقي العقد تدريجياً وتشغيلها. في هذه الحالة ، لا يلزم إجراء ترقية Occ! تحتاج فقط إلى استبدال ملفات php وحفظ التكوين.
عند النسخ الاحتياطي ، تحتاج إلى إيقاف النسخ المتماثل إلى Slave وإجراء تفريغ متزامن لبيانات التعريف من قاعدة البيانات في وقت واحد مع إنشاء لقطة من الملفات الموجودة في التخزين. تحتاج إلى تخزينها في أزواج. يجب إجراء الاسترداد بالمثل من تفريغ قاعدة البيانات والملفات لنفس الفترة. بخلاف ذلك ، يكون فقدان البيانات ممكنًا ، نظرًا لأن الملف قد يكون في التخزين ، ولكن لا يحتوي على بيانات وصفية في قاعدة البيانات.
