ordenado logistica fisica carga almacenamiento almacenaje php file upload storage

php - logistica - almacenamiento de carga fisica



Estrategias de almacenamiento de carga de imágenes (7)

Cuando un usuario carga una imagen en mi sitio, la imagen pasa por este proceso;

  • usuario sube fotos
  • almacene los metadatos de la imagen en db, dando a la imagen un ID único
  • Procesamiento de imágenes asíncronas (creación de miniaturas, recorte, etc.)
  • Todas las imágenes se almacenan en la misma carpeta de cargas.

Hasta ahora, el sitio es bastante pequeño, y solo hay ~ 200,000 imágenes en el directorio de subidas. Me doy cuenta de que no estoy ni cerca del límite físico de los archivos dentro de un directorio, pero este enfoque claramente no se escalará, así que me preguntaba si alguien tenía algún consejo sobre las estrategias de carga / almacenamiento para manejar grandes volúmenes de carga de imágenes.

EDITAR: Crear subcarpetas de nombre de usuario (o más específicamente, ID de usuario) parece ser una buena solución. Con un poco más de excavación, he encontrado una gran información aquí; Cómo almacenar imágenes en tu sistema de archivos
Sin embargo, ¿este enfoque de ID de usuario se escalaría bien si se comprara un CDN en la ecuación?


¿Has pensado en usar algo como Amazon S3 para almacenar los archivos? Dirijo una empresa de alojamiento de fotos y, luego de alcanzar rápidamente los límites en nuestro propio servidor, cambiamos a AmazonS3. La belleza de S3 es que no hay límites como los inodos y, qué no, simplemente le sigues lanzando archivos.

Además: si no te gusta S3, siempre puedes intentar dividirlo en subcarpetas tanto como puedas:

/userid/year/month/day/photoid.jpg


He respondido una pregunta similar antes, pero no puedo encontrarla, tal vez el OP eliminó su pregunta ...

De todos modos, la solución de Adams parece ser la mejor hasta ahora, pero no es a prueba de balas, ya que las images/c/cf/ (o cualquier otro par dir / subdir) aún pueden contener hasta 16 ^ 30 hashes únicos y al menos 3 veces más archivos si contamos las extensiones de imagen, mucho más de lo que cualquier sistema de archivos normal puede manejar.

AFAIK, SourceForge.net también usa este sistema para repositorios de proyectos, por ejemplo, el proyecto "fatfree" se ubicaría en los projects/f/fa/fatfree/ , sin embargo, creo que limitan los nombres de los proyectos a 8 caracteres.

Almacenaría el hash de la imagen en la base de datos junto con un campo DATE / DATETIME / TIMESTAMP indica cuándo se cargó / procesó la imagen y luego colocaría la imagen en una estructura como esta:

images/ 2010/ - Year 04/ - Month 19/ - Day 231c2ee287d639adda1cdb44c189ae93.png - Image Hash

O:

images/ 2010/ - Year 0419/ - Month & Day (12 * 31 = 372) 231c2ee287d639adda1cdb44c189ae93.png - Image Hash

Además de ser más descriptiva, esta estructura es suficiente para albergar cientos de miles (dependiendo de los límites de su sistema de archivos) de imágenes por día durante varios miles de años . Esta es la forma en que Wordpress y otros lo hacen, y creo que lo lograron. uno.

Las imágenes duplicadas se pueden consultar fácilmente en la base de datos y solo tendría que crear enlaces simbólicos.

Por supuesto, si esto no es suficiente para usted, siempre puede agregar más subdirectorios (horas, minutos, ...).

Personalmente, no usaría ID de usuario a menos que no tenga esa información disponible en su base de datos porque:

  1. Divulgación de nombres de usuario en la URL
  2. Los nombres de usuario son volátiles (puede cambiar el nombre de las carpetas, pero aún así ...)
  3. Un usuario puede cargar hipotéticamente una gran cantidad de imágenes.
  4. No sirve para nada (?)

Respecto al CDN no veo ninguna razón por la que este esquema (o cualquier otro) no funcionara ...


MediaWiki genera la suma MD5 del nombre del archivo cargado y utiliza las dos primeras letras de la MD5 (por ejemplo, "c" y "f" de la suma "cf1e66b77918167a6b6b972c12b1c00d") para crear esta estructura de directorio:

images/c/cf/Whatever_filename.png

También puede usar la ID de imagen para un límite superior predecible en la cantidad de archivos por directorio. Tal vez tome floor(image unique ID / 1000) para determinar el directorio principal, para 1000 imágenes por directorio.


Puede considerar el código abierto http://danga.com/mogilefs/ ya que es perfecto para lo que está haciendo. Le llevará a pensar en las carpetas a los espacios de nombres (que podrían ser usuarios) y le permitirá almacenar sus imágenes por usted. La mejor parte es que no tiene que preocuparse por cómo se almacenan los datos. Lo hace completamente redundante e incluso puede establecer controles en torno a qué tan reducidas son las miniaturas también.


Puede convertir un nombre de usuario a md5 y establecer una carpeta de 2-3 primeras letras de nombre de usuario convertido md5 para los avatares y para las imágenes que puede convertir y jugar con tiempo, cadenas aleatorias, identificadores y nombres.

8648b8f3ce06a7cc57cf6fb931c91c55 - devcline

También una primera letra del nombre de usuario o id para la siguiente carpeta o inversa

Se verá como

Estructura:

stream/img/86/8b8f3ce06a7cc57cf6fb931c91c55.png //simplest stream/img/d/2/0bbb630d63262dd66d2fdde8661a410075.png //first letter and id folders stream/img/864/d/8b8f3ce06a7cc57cf6fb931c91c55.png // with first letter of the nick stream/img/864/2/8b8f3ce06a7cc57cf6fb931c91c55.png //with unique id stream/img/2864/8b8f3ce06a7cc57cf6fb931c91c55.png //with unique id in 3 letters stream/img/864/2_8b8f3ce06a7cc57cf6fb931c91c55.png //with unique id in picture name

Código

$username = substr($username_md5, 1); // to cut first letter from the md5 converted nick $username_first = $username[0]; // the first letter $username_md5 = md5($username); // md5 for username $randomname = uniqid($userid).md5(time()); //for generate a random name based on ID

Puedes probar también con base64

$image_encode = strtr(base64_encode($imagename), ''+/='', ''-_,''); $image_decode = base64_decode(strtr($imagename, ''-_,'', ''+/=''));

Steam y dokuwiki utilizan esta estructura.


Sí, sí, sé que este es un tema antiguo. Pero el problema es almacenar gran cantidad de imágenes y cómo debe organizarse la estructura de carpetas subyacente. Así que presento mi manera de manejarlo con la esperanza de que esto pueda ayudar a algunas personas.

La idea de utilizar el hash md5 es la mejor manera de manejar el almacenamiento masivo de imágenes. Teniendo en cuenta que diferentes valores pueden tener el mismo hash, sugiero encarecidamente agregar también el ID de usuario o nicname a la ruta para que sea único. Sí, eso es todo lo que se necesita. Si alguien tiene diferentes usuarios con el mismo ID de base de datos, bueno, hay algo mal;) Por root_path/md5_hash/user_id tanto, root_path/md5_hash/user_id es todo lo que necesita para hacerlo correctamente.

El uso de DATE / DATETIME / TIMESTAMP no es la solución óptima por cierto IMO. Usted termina con grandes grupos de carpetas de imágenes en un día de trabajo y casi vacías en las menos frecuentadas. No estoy seguro de que esto lleve a problemas de rendimiento, pero hay algo como la estética de los datos y una distribución de datos consistente siempre es superior.

Así que claramente voy por la solución de hash.

Escribí la siguiente función para facilitar la generación de dichas rutas de almacenamiento basadas en hash. Siéntete libre de usarlo si te gusta.

/** * Generates directory path using $user_id md5 hash for massive image storing * @author Hexodus * @param string $user_id numeric user id * @param string $user_root_raw root directory string * @return null|string */ function getUserImagePath($user_id = null, $user_root_raw = "images/users", $padding_length = 16, $split_length = 3, $hash_length = 12, $hide_leftover = true) { // our db user_id should be nummeric if (!is_numeric($user_id)) return null; // clean trailing slashes $user_root_rtrim = rtrim( $user_root_raw, ''///' ); $user_root_ltrim = ltrim( $user_root_rtrim, ''///' ); $user_root = $user_root_ltrim; $user_id_padded = str_pad($user_id, $padding_length, "0", STR_PAD_LEFT); //pad it with zeros $user_hash = md5($user_id); // build md5 hash $user_hash_partial = $hash_length >=1 && $hash_length < 32 ? substr($user_hash, 0, $hash_length) : $user_hash; $user_hash_leftover = $user_hash_partial <= 32 ? substr($user_hash, $hash_length, 32) : null; $user_hash_splitted = str_split($user_hash_partial, $split_length); //split in chunks $user_hash_imploded = implode($user_hash_splitted,"/"); //glue aray chunks with slashes if ($hide_leftover || !$user_hash_leftover) $user_image_path = "{$user_root}/{$user_hash_imploded}/{$user_id_padded}"; //build final path else $user_image_path = "{$user_root}/{$user_hash_imploded}/{$user_hash_leftover}/{$user_id_padded}"; //build final path plus leftover return $user_image_path; }

Prueba de función de llamadas:

$user_id = "1394"; $user_root = "images/users"; $user_hash = md5($user_id); $path_sample_basic = getUserImagePath($user_id); $path_sample_advanced = getUserImagePath($user_id, "images/users", 8, 4, 12, false); echo "<pre>hash: {$user_hash}</pre>"; echo "<pre>basic:<br>{$path_sample_basic}</pre>"; echo "<pre>customized:<br>{$path_sample_advanced}</pre>"; echo "<br><br>";

La salida resultante - coloreada para su conveniencia;):


Tengo alma que estoy usando durante mucho tiempo. Es un código bastante antiguo, y puede optimizarse aún más, pero todavía funciona bien como está.

Es una función inmutable que crea una estructura de directorios basada en:

  1. Número que identifica la imagen (ID. ARCHIVO):

se recomienda que este número sea único para el directorio base, como la clave principal para la tabla de la base de datos, pero no es obligatorio.

  1. El directorio base

  2. El número máximo deseado de archivos y subdirectorios de primer nivel. Esto prometido solo se puede mantener si cada ID de ARCHIVO es único.

Ejemplo de uso:

Usando explícitamente ID de ARCHIVO:

$fileName = ''my_image_05464hdfgf.jpg''; $fileId = 65347; $baseDir = ''/home/my_site/www/images/''; $baseURL = ''http://my_site.com/images/''; $clusteredDir = /DirCluster::getClusterDir( $fileId ); $targetDir = $baseDir . $clusteredDir; $targetPath = $targetDir . $fileName; $targetURL = $baseURL . $clusteredDir . $fileName;

Usando el nombre del archivo, número = crc32 (nombre de archivo)

$fileName = ''my_image_05464hdfgf.jpg''; $baseDir = ''/home/my_site/www/images/''; $baseURL = ''http://my_site.com/images/''; $clusteredDir = /DirCluster::getClusterDir( $fileName ); $targetDir = $baseDir . $clusteredDir; $targetURL = $baseURL . $clusteredDir . $fileName;

Código:

class DirCluster { /** * @param mixed $fileId - numeric FILE ID or file name * @param int $maxFiles - max files in one dir * @param int $maxDirs - max 1st lvl subdirs in one dir * @param boolean $createDirs - create dirs? * @param string $path - base path used when creatign dirs * @return boolean|string */ public static function getClusterDir($fileId, $maxFiles = 100, $maxDirs = 10, $createDirs = false, $path = "") { // Value for return $rt = ''''; // If $fileId is not numerci - lets create crc32 if (!is_numeric($fileId)) { $fileId = crc32($fileId); } if ($fileId < 0) { $fileId = abs($fileId); } if ($createDirs) { if (!file_exists($path)) { // Check out the rights - 0775 may be not the best for you if (!mkdir($path, 0775)) { return false; } @chmod($path, 0775); } } if ( $fileId <= 0 || $fileId <= $maxFiles ) { return $rt; } // Rest from dividing $restId = $fileId%$maxFiles; $formattedFileId = $fileId - $restId; // How many directories is needed to place file $howMuchDirs = $formattedFileId / $maxFiles; while ($howMuchDirs > $maxDirs) { $r = $howMuchDirs%$maxDirs; $howMuchDirs -= $r; $howMuchDirs = $howMuchDirs/$maxDirs; $rt .= $r . ''/''; // DIRECTORY_SEPARATOR = / if ($createDirs) { $prt = $path.$rt; if (!file_exists($prt)) { mkdir($prt); @chmod($prt, 0775); } } } $rt .= $howMuchDirs-1; if ($createDirs) { $prt = $path.$rt; if (!file_exists($prt)) { mkdir($prt); @chmod($prt, 0775); } } $rt .= ''/''; // DIRECTORY_SEPARATOR return $rt; } }