
¡Buena salud, Hawkers! En el proceso de trabajar en un proyecto de sitio de citas, se hizo necesario organizar el almacenamiento de las fotos de los usuarios. Según los términos de referencia, el número de fotos por usuario está limitado a 10 archivos. Pero puede haber decenas de miles de usuarios. Especialmente considerando que el proyecto en su forma actual ya existe desde el comienzo del "cero". Es decir, ya hay miles de usuarios en la base de datos. Casi cualquier sistema de archivos, que yo sepa, reacciona muy negativamente a una gran cantidad de nodos secundarios en una carpeta. Por experiencia, puedo decir que los problemas comienzan después de 1000-1500 archivos / carpetas en la carpeta principal.
Descargo de responsabilidad. Busqué en Google antes de escribir el artículo y encontré varias soluciones al problema en discusión (por ejemplo, aquí o aquí ). Pero no encontré una solución única que coincida exactamente con la mía. Además, en este artículo solo comparto mi propia experiencia para resolver el problema.Teoría
Además de la tarea de almacenamiento como tal, también había una condición en la declaración de trabajo, según la cual era necesario dejar subtítulos y subtítulos para las fotos. Por supuesto, no puedes prescindir de una base de datos. Es decir, lo primero que hacemos es crear una tabla en la que prescribimos la asignación de metadatos (firmas, títulos, etc.) con los archivos en el disco. Cada archivo corresponde a una fila en la base de datos. En consecuencia, cada archivo tiene un identificador.
Una pequeña digresión. Hablemos de auto-incremento. Un sitio de citas puede tener una docena o dos mil usuarios. La pregunta es cuántos usuarios generalmente pasan por el proyecto durante todo el tiempo de su existencia. Por ejemplo, la audiencia activa de citas-ru es varios cientos de miles. Sin embargo, solo imagine cuántos usuarios quedaron durante la vida útil de este proyecto; cuántos usuarios no están activados hasta ahora. Y ahora agregue nuestra legislación, que nos obliga a almacenar información sobre los usuarios durante al menos seis meses ... Tarde o temprano, 4 con un centavo de
INT NO FIRMADO finalizará. Por lo tanto, es mejor tomar
BIGINT para la clave primaria.
Ahora intentemos imaginar un número de tipo
BIGINT . Esto es de 8 bytes. Cada byte es de 0 a 255. 255 nodos secundarios son bastante normales para cualquier sistema de archivos. Es decir, tomamos el identificador de archivo en representación hexadecimal, lo dividimos en fragmentos de dos caracteres. Usamos estos fragmentos como nombres de carpeta, este último como el nombre del archivo físico. BENEFICIOS!
0f/65/84/10/67/68/19/ff.file
Elegante y simple La extensión del archivo no es importante aquí. De todos modos, el archivo será proporcionado por un script que le dará al navegador un tipo MIME particular, que también almacenaremos en la base de datos. Además, almacenar información sobre el archivo en la base de datos le permite redefinir la ruta para el navegador. Digamos que el archivo que tenemos está ubicado en relación con el directorio del proyecto a lo largo de la ruta
/content/files/0f/65/84/10/67/68/19/ff.file
. Y en la base de datos puede escribirle una URL, por ejemplo,
/content/users/678/files/somefile
. Los SEO probablemente sonríen bastante en este momento. Todo esto nos permite no preocuparnos más sobre dónde colocar el archivo físicamente.
Tabla en la base de datos
Además del identificador, tipo MIME, URL y ubicación física, almacenaremos archivos md5 y sha1 en la tabla para filtrar los mismos archivos si es necesario. Por supuesto, también necesitamos almacenar las relaciones entre entidades en esta tabla. Suponga la ID de usuario a la que pertenecen los archivos. Y si el proyecto no es muy grande, entonces en el mismo sistema podemos almacenar, por ejemplo, fotografías de productos. Por lo tanto, también almacenaremos el nombre de la clase de entidad a la que pertenece el registro.
Hablando de pájaros. Si cierra la carpeta usando .htaccess para acceso externo, el archivo solo se puede obtener a través del script. Y en el script será posible determinar el acceso al archivo. Mirando hacia el futuro un poco, diré que en mi CMS (donde el proyecto mencionado se está cortando ahora) el acceso está determinado por grupos de usuarios básicos, de los cuales tengo 8: invitados, usuarios, gerentes, administradores, inactivos, bloqueados, eliminados y súper administradores. Super-admin puede hacer absolutamente todo, por lo que no participa en la determinación del acceso. Si el usuario tiene un indicador de superadministrador, entonces es un superadministrador. Todo es simple Es decir, determinaremos el acceso a los siete grupos restantes. El acceso es simple: dar el archivo o no dar. En total, puede tomar un campo de tipo
TINYINT .
Y una cosa más. De acuerdo con nuestra ley, tendremos que almacenar físicamente imágenes personalizadas. Es decir, necesitamos marcar de alguna manera las imágenes como eliminadas, en lugar de eliminarlas físicamente. Es más conveniente utilizar un campo de bits para estos fines. En tales casos, generalmente uso un campo de tipo
INT . Para reservar, por así decirlo. Además, tengo una tradición ya establecida de colocar la bandera
BORRADO en el quinto bit desde el final. Pero ya no importa otra vez.
¿Qué tenemos como resultado?
create table `files` ( `id` bigint not null auto_increment,
Clase de despachador
Ahora necesitamos crear una clase con la cual cargaremos archivos. La clase debe proporcionar la capacidad de crear archivos, reemplazar / modificar archivos, eliminar archivos. Además, vale la pena considerar dos puntos. En primer lugar, el proyecto se puede transferir de un servidor a otro. Entonces, en la clase, debe definir una propiedad que contenga el directorio raíz de los archivos. En segundo lugar, será muy desagradable si alguien golpea una tabla en la base de datos. Por lo tanto, debe prever la posibilidad de recuperación de datos. Con el primero, todo está generalmente claro. En cuanto a la copia de seguridad de datos, reservaremos solo lo que no se pueda restaurar.
ID : restaurado desde la ubicación física del archivo
entidad_tipo : no restaurado
entidad - no restaurada
mime - restaurado usando la extensión finfo
md5 : se restaura desde el archivo en sí
sha1 : restaurado desde el archivo mismo
archivo : restaurado desde la ubicación física del archivo
url - no restaurado
meta - no restaurado
tamaño : restaurado desde el archivo en sí
creado : puede tomar información de un archivo
actualizado : puede tomar información de un archivo
acceso - no restaurado
banderas - no restauradas
Puede descartar inmediatamente la metainformación. No es crítico para el funcionamiento del sistema. Y para una recuperación más rápida, aún necesita guardar el tipo MIME. Total: tipo de entidad, ID de entidad, MIME, URL, acceso e indicadores. Para aumentar la confiabilidad del sistema, almacenaremos la información de respaldo para cada carpeta de destino por separado en la carpeta misma.
Código de clase <?php class BigFiles { const FLAG_DELETED = 0x08000000;
Considere algunos puntos:
-
realRoot : la ruta completa a la carpeta con el sistema de archivos que termina en una barra inclinada.
-
webRoot : la ruta desde la raíz del sitio sin una barra inclinada (ver a continuación para saber por qué).
- Como DBMS, uso la extensión
MySQLi .
- De hecho, la información de la matriz
$ _FILES se pasa como el primer argumento para el método de
carga .
- Si llama al método de
actualización para pasar la ID de un archivo existente, se reemplazará si la matriz de entrada en
tmp_name no está vacía.
- Puede eliminar y cambiar las banderas de los archivos varias a la vez. Para hacer esto, en lugar de pasar el identificador de archivo, debe pasar una matriz con identificadores o una cadena con ellos, separados por comas.
Enrutamiento
En realidad, se reduce a unas pocas líneas en htaccess en la raíz del sitio (se supone que mod_rewrite está habilitado):
RewriteCond %{REQUEST_URI} ^/content/(.*)$ RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.+)$ content/index.php?file=$1 [L,QSA]
"Contenido" es la carpeta en la raíz del sitio en mi caso. Por supuesto, puede nombrar la carpeta de manera diferente. Y, por supuesto, index.php en sí, almacenado en mi caso en la carpeta de contenido:
<?php $dbHost = '127.0.0.1'; $dbUser = 'user'; $dbPass = '****'; $dbName = 'database'; try { if (empty($_REQUEST['file'])) { header('HTTP/1.1 400 Bad Request'); exit; } $userG = 'anonimous';
Bueno, por sí mismo, cerramos el sistema de archivos del acceso externo. Coloque el archivo
.htaccess
en la raíz de la carpeta de
content/files
con solo una línea:
Deny from all
Resumen
Esta solución le permite evitar la pérdida del rendimiento del sistema de archivos debido a un aumento en la cantidad de archivos. Al menos, definitivamente se pueden evitar los problemas en forma de miles de archivos en una carpeta. Y al mismo tiempo, podemos organizar y controlar el acceso a los archivos en direcciones legibles por humanos. Además del cumplimiento de nuestra legislación sombría. Haga una reserva de inmediato, esta solución NO es una forma completa de proteger el contenido. Recuerde: si algo se reproduce en el navegador, se puede descargar de forma gratuita.