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.



Un hack de malade!

5 06 2008

Alors que je cherchais des modules Ajax, ou un framework qui m’aide sous PHP, je suis tombé sur un truc de malades. Un petit script pas idiot du tout permet de vérifier via un serveur web la liste des sites “sociaux” que le visiteur a consultés récemment. En vérité, il vérifie si vous avez visité une liste de sites prédéfinie.

Le but de cela? En fin de chaque message, sur mon blog, vous avez un petit lien qui permet de poster le permalien de cet article sur l’un ou l’autre site social. Facebook, Digg, ou d’autres. Mais le fait est que si vous n’allez jamais sur certains de ces sites, le lien en question ne sert à rien. Donc pourquoi ne pas se limiter à n’afficher que les liens vers les sites que vous fréquentez souvent?

L’astuce consiste à créer une petite iframe dans laquelle on place un lien vers chacun des sites de la liste. Ensuite, on ajoute une micro feuille de style qui masque ou affiche les liens selon qu’ils soient visités ou non par la personne… Enfin, il suffit de vérifier si le lien vers un certain site est affiché ou non, via un script, et voilà. Le tour est joué.

L’idée n’est pas stupide… Après tout, quand une liste de liens est affichée sur une page web “normale”, à l’ancienne, le lien est bleu si l’on est jamais allé sur le site lié, et violet si il a déjà été visité! Il suffisait (ahem) de faire un script pour vérifier cela côté serveur.

Dans mon cas, je vais utiliser ce script, modifié, pour mon projet actuel… Cela me permettra de poser les bonnes questions à mes membres! En effet, pourquoi demander “Quel est ton pseudo sur Last.fm?” à quelqu’un qui n’y a jamais mis les pieds??? Le script est ici



La télécommande BD PS3 sous Linux

29 04 2008

Je suis tombé par hasard (enfin, après une recherche assez vague sur Google) sur un script en python qui permet de capturer les signaux envoyés par la télécommande BD pour la PS3. Cette télécommande fonctionnant en Bluetooth, elle fait une télécommande de choix pour contrôler de façon simple et efficace tout média center digne de ce nom… Bien mieux que l’infrarouge vu que ça fonctionne même si un objet non transparent se trouve sur le chemin entre la télécommande et l’appareil à contrôler, et aussi il est plus facile de trouver des capteurs bluetooth bon-marché de nos jours que de trouver un capteur IR pas trop cher et efficace… La grande difficulté dans le cas de l’IR restant de trouver un module qui puisse être placé — si possible de façon discrète — sur un media center de façon à ce qu’il soit visible depuis l’avant de l’appareil sans pour autant faire tache.

De toute façon, le problème ne se pose pas sur la télécommande de la PS3. Les instructions se trouvent dans la suite (en attendant un tutoriel dans les prochains jours sur ce site)

Lire la suite »