Redimensionner une image au vol

1 01 2009

Comme vous le savez, je développe en ce moment une application Facebook. Dans cette application, je devrai afficher des images un peu partout, issues d’une sorte de base de données. Pour ne pas révéler le contenu de mon application, supposons qu’il s’agisse de pochettes de CDs. Je dispose donc d’un répertoire dans lequel se trouvent des milliers d’images de pochettes, généralement en bonne qualité, assez grandes. Mais un peu comme sur last.fm, je devrais pouvoir afficher une série de pochettes de taille plus petite, disons 64 pixels de large.

Pour redimesionner ces images, j’ai deux possibilités:

  • Faire un traitement en batch avec un logiciel conçu pour des conversions en séries.
  • Convertir au vol ces images via GD ou ImageMagick

La première solution ne me convient pas, car de nouvelles pochettes seront ajoutées au fil du temps, et je ne veux pas être obligé de refaire une conversion de temps à autres. La seconde est gourmande en ressources, la conversion d’images prend un grand nombre de cycles processeur, et s’il y a ne serait-ce que 50 personnes qui veulent afficher une page contenant 20 de ces images, le serveur risque de planter.

L’idéal est un mélange entre ces deux solutions. Le redimensionnement de l’image par ImageMagick (ou GD) suivi de son stockage dans un autre répertoire. Mon script va donc faire cela. Cependant cela donne lieu à un problème technique. Comment savoir si l’image a déjà été convertie auparavant?

Ma solution à ce problème est tellement logique qu’elle ne saute pas aux yeux : ne lancer le script de redimensionnement que si l’image stockée n’existe pas.

En pseudo-code, cela donnerait ceci:

1
2
3
4
5
if(is_file($fichier_demandé)) {
  envoyer_le_fichier();
} else {
  generer_le_fichier_redimensionne();
}

Mais cela voudrait dire qu’on lance un script php et fait un accès au disque pour vérifier si le fichier existe ou pas. Il y a mieux à faire…

Au lieu de faire ce script qui vérifie si le fichier existe ou pas, ne lancer ce script qu’a condition que le fichier n’existe pas à l’endroit attendu. Et comment faire en sorte que cela soit automatisé, ou géré par le serveur lui-même?

ErrorDocument 404 404.php

Toutes mes images sont stockées dans le répertoire /i/ de ma racine de site web. Donc j’ai mis cette directive dans /i/.htaccess.

Mon script n’est ainsi appelé que si l’image n’existe pas. Et au lieu d’afficher le sempiternel message d’erreur, je corrige cette erreur en créant le fichier redimensionné. D’autant que vu qu’on appelle une image, afficher un “message d’erreur” ne sert à rien…

Example:

1
<img src="/i/w=64/pochette/Aphex-Twin/Hangable-Auto-Bulb.jpg" />

Code source de 404.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php
/* Initialisation des variables */
$requestedWidth = 0;    // Largeur demandée
$requestedHeight= 0;   // Hauteur demandée
$filename='';            // Nom du fichier original (avec chemin)
$requestedFilename = ''; // Nom de fichier redimensionné (avec chemin)
$basePath = '/var/foo/mon_site/';

/* Parcours de l'url demandée */
if(ereg("/w=([0-9]+)/", $_SERVER['REQUEST_URI'], $regs)) {
    $requestedWidth = (int)$regs[1];
}
unset($regs);
if(ereg('/h=([0-9]+)/', $_SERVER['REQUEST_URI'], $regs)) {
    $requestedHeight = (int)$regs[1];
}
unset($regs);

/* Détermine le fichier à lire */
ereg('i/[wh]{1}=[0-9]*/([a-zA-Z0-9/-\.]*)', $_SERVER['REQUEST_URI'], $regs);
if(!is_file($basePath.'i/'.$regs[1])) {
    error_log("The path doesn't lead to a file: ".$basePath.$regs[1]);
}
if(!is_readable($basePath.'i/'.$regs[1])) {
    error_log('The file is not readable:'.$basePath.$regs[1]);
}
$filename = $basePath.'i/'.$regs[1];
unset($regs);

/* Détermine le fichier à créer */
ereg('(i/[wh]{1}=[0-9]*/[a-zA-Z0-9/-\.]*)', $_SERVER['REQUEST_URI'], $regs);
$requestedFilename = $basePath.$regs[1];
unset($regs);

$image = new Imagick();
$image->readImage($filename);
$image->resizeImage($requestedWidth, $requestedHeight, imagick::FILTER_LANCZOS, 1);
if(!is_dir(dirname($requestedFilename))) {
    mkdir(dirname($requestedFilename), 0777, true);
}
$image->writeImage($requestedFilename);

header('Content-Type: image/'.$image->getImageFormat());
echo $image;

?>

N’oubliez pas, si vous utilisez ce script, que sur mon serveur, toutes les images sont dans /var/foo/mon_site/i/. Aussi, vu que ce répertoire ne contient QUE des images, je ne vérifie pas si le visiteur demande une image ou une page web normale… Pensez donc à faire cette vérification si vous utilisez ce script en tant que page 404 sur tout le serveur, au lieu de limiter l’emploi de celle-ci à un répertoire donné via un .htaccess ou une directive dans votre .conf Apache…

Enfin, une demande vers l’image donnée en exemple créera un répertoire /var/foo/mon_site/i/w=64/pochette/……

Dernière considération: ce script ne peut gérer le redimensionnement que selon la largeur OU la hauteur, et redimensionne en conservant le rapport largeur/hauteur original.



Je développe une application Facebook

21 12 2008

Ou au moins, j’essaie de le faire… L’api est asse puissante, plutôt compréhensible, et je pense que je vais bien m’amuser. Ceci étant, je ne suis pas certain que mon serveur tout pourri suffise à faire tourner la chose. Pas grave. M’en fiche. Si ca devient pratique, je ferai héberger ça sur un serveur digne de ce nom.

En tout cas, une chose est claire: la sécurité est assez poussée. Leur moteur réécrit tout le code javascript et les divers éléments DOM pour éviter qu’on puisse s’en servir pour piocher dans les informations privées des utilisateurs. Et leur serveur joue un rôle de proxy. Bien pensé.

Je vous donnerai des infos dès que j’aurai fait quelquechose d’un tant soit peu évolué et fonctionnel. Si vous avez envie de me filer un coup de main, et que vous aimez les jeux vidéos, n’hésitez pas à vous faire connaître!



Par où commencer?

11 06 2008

Comme je l’ai dit il y a quelques jours, je repèche une de mes vieilles idées de site pour la concrétiser en un site social, vu que c’est la mode.

En commençant ce projet, j’avais pas mal d’idées en tête. Des prérequis, aussi. Et quelques contraintes. Le concept de base utilisera pas mal d’Ajax. Mais en donnant une alternative non-ajax pour ne pas rejeter les gens qui bloquent JavaScript, ou les moteurs de recherche. Car les moteurs de recherche sont aussi l’un des objectifs de ce projet.

Bref. Il me faut des outils de base. Simplement parce qu’il ne sert à rien de réinventer la roue. Parce que le web évolue beaucoup plus vite que ce que ne peut suivre un seul programmeur. Et parce que je suis seul à élaborer ce projet. J’ai demandé son soutien à un premier ami, qui m’a répondu “à quoi ça sert?” et qui n’adhère pas à ce concept. J’ai demandé son soutien à un collègue de travail qui clamait avoir les mêmes connaissances que moi, mais je me suis apperçu que ses connaissances ne sont pas vraiment dans les mêmes domaines que les miens. J’ai ensuite demandé son aide à un bloggueur avec qui je m’entend pas mal, issu d’une université informatique, et qui partage mon goût du deux-point-zéro, mais il entre dans la vie active avec un élan si fort qu’il lui sera impossible d’accorder le temps que ce projet requiert. Lire la suite »