Archivo

Entradas Etiquetadas ‘Content Delivery Network’

Sencillo Mirror de imágenes para tu Blog usando Google App Engine

Lunes, 25 de Enero de 2010

Muchos de los Hostings que usamos para albergar nuestras webs tienen una limitación de ancho de banda mensual. Por tanto es bastante interesante disponer de un sistema de almacenamiento de imágenes que nos permita disponer de dichos archivos sin consumir ancho de banda de nuestro posting. En este artículo voy a explicar como realizar una pequeña aplicación para tener nuestro mirror de imágenes.

En este articulo no voy a dar por echo que el lector conoce y se maneja en el entorno Google App Engine por tanto me centrare en explicar el código utilizado.

main.py

import cgi
import os
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import google.appengine.ext.webapp.util
from google.appengine.ext import db
from google.appengine.ext.webapp import template

class Mirror(db.Model):
 name = db.TextProperty()
 mime = db.TextProperty()
 picture = db.BlobProperty(default=None)

class GetImage(webapp.RequestHandler):
 def get(self,name):
  image = getImageFile(name)
  if (movie and movie.picture):
   self.response.headers['Content-Type'] = image.mime
   self.response.out.write(image.picture)
  else:
   self.response.out.write(“<!DOCTYPE HTML PUBLIC \”-//IETF//DTD HTML 2.0//EN\”>\r\n”)
   self.response.out.write(“<html>\r\n\t<head>\r\n”)
   self.response.out.write(“\t\t<title>404 Not Found</title>\r\n”)
   self.response.out.write(“\t</head>\r\n\t<body>\r\n”)
   self.response.out.write(“\t\t<h1>Not Found</h1>\r\n”)
   self.response.out.write(“\t\t<p>The requested URL /%s was not found on this server.</p>\r\n”%name)
   self.response.out.write(“\t</body>\r\n</html>\r\n”)

def getImageFile(name):
 images = Mirror.all()
 for image in images:
  if image.name == name:
   return image
 return None

class MainHandler(webapp.RequestHandler):

 def get(self):
  images = Mirror.all()
  template_values = {
   ’error’: “”,
   ’images’: images,
  }
  path = os.path.join(os.path.dirname(__file__), ‘form.html’)
  self.response.out.write(template.render(path, template_values))

 def post(self):
  newimage = Mirror()
  imagedata = self.request.get(“image”)
  newimage.picture = db.Blob(imagedata)
  newimage.name = self.request.POST[u'image'].filename
  newimage.mime = self.request.POST[u'image'].type
  newimage.put()
  self.redirect(“/”)

def main():
 application = webapp.WSGIApplication([('/', MainHandler),(r'/(.*)', GetImage)],debug=True)
 run_wsgi_app(application)

if __name__ == ‘__main__’:
 main()

Comos sabéis la aplicación que subimos a los servidores de Google corren en una SandBox donde no es posible escribir en disco, por tanto usaremos el sistema de almacenamiento de GAE para almacenar las imágenes. Por tanto primero de todo deberemos definir un modelo de datos.

class Mirror(db.Model):
 name = db.TextProperty()
 mime = db.TextProperty()
 picture = db.BlobProperty(default=None)

En este caso definimos la estructura Mirror con sus propiedades name (nombre de la imagen), mime (mime de la imagen) y picture (la imagen en bruto).

Comos sabéis el sistema de paths de GAE es gestionado a través de SGIApplication por tanto para poder acceder a las imágenes almacenadas deberemos definir una política de paths. En este caso he definido la siguiente:

application = webapp.WSGIApplication([('/', MainHandler),(r'/(.*)', GetImage)],debug=True)
run_wsgi_app(application)

Es decir en caso de acceder a / se nos mostrara la pagina principal a través de la clase MainHandler y en caso de que se nos muestre / se nos mostrara el archivos a través de la classe GetImage.

La Clase MainHandler

class MainHandler(webapp.RequestHandler):

def get(self):
 images = Mirror.all()
 template_values = {
  ’error’: “”,
  ’images’: images,
 }
 path = os.path.join(os.path.dirname(__file__), ‘form.html’)
 self.response.out.write(template.render(path, template_values))

def post(self):
 newimage = Mirror()
 imagedata = self.request.get(“image”)
 newimage.picture = db.Blob(imagedata)
 newimage.name = self.request.POST[u'image'].filename
 newimage.mime = self.request.POST[u'image'].type
 newimage.put()
 self.redirect(“/”)

La clase MainHandler en caso de se accedida mediante GET mostrara el formulario HTML (form.html) y un listado de imágenes almacenadas, para hacer mas legible el código he optado por usar la funcionalidad de platillas que ofrece GAE.

El formulario envía su mismo path la imagen utilizando el método POST por tanto en el tetrodo POST de la clase MainHandler donde se almacenara la imagen enviada.

La Clase GetImage

class GetImage(webapp.RequestHandler):
 def get(self,name):
 image = getImageFile(name)
 if (movie and movie.picture):
  self.response.headers['Content-Type'] = image.mime
  self.response.out.write(image.picture)
 else:
  self.response.out.write(“<!DOCTYPE HTML PUBLIC \”-//IETF//DTD HTML 2.0//EN\”>\r\n”)
  self.response.out.write(“<html>\r\n\t<head>\r\n”)
  self.response.out.write(“\t\t<title>404 Not Found</title>\r\n”)
  self.response.out.write(“\t</head>\r\n\t<body>\r\n”)
  self.response.out.write(“\t\t<h1>Not Found</h1>\r\n”)
  self.response.out.write(“\t\t<p>The requested URL /%s was not found on this server.</p>\r\n”%name)
  self.response.out.write(“\t</body>\r\n</html>\r\n”)

La clase getImage recoge en el parámetro nombre de la función get el resultado de la expresión regular definida en el path r’(.*)’. Por tanto en este caso name será el nombre del archivo. Mediante la función getImageFile recorremos la base de datos y buscamos el archivo solicitado.

def getImageFile(name):
 images = Mirror.all()
 for image in images:
  if image.name == name:
   return image
 return None

Si existe el archivo este es mostrado modificando la cabecera Content-Type con el mime del archivo para que el navegador lo muestre correctamente. En caso contrario se muestra un mensaje de error.

form.html

<!DOCTYPE HTML PUBLIC “-//IETF//DTD HTML 2.0//EN”>
<html>
<head>
<title>Upload File</title>
</head>
<body>
<h1>Select File to Upload</h1>
<form action=”" method=”POST” enctype=”multipart/form-data” >
<input name=”image” type=”file” size=”42″ />
<input type=”submit” value=”Upload”>
</form>
<H1>Index of Files</H1>
<PRE>
<IMG SRC=”/blank.gif” ALT=”     “> <A HREF=”?N=D”>Name</A>
<HR>
{% for image in images %}
<IMG SRC=”/image2.gif” ALT=”[IMG]“> <A HREF=”/{{image.name}}”>{{image.name}}</A>
{% endfor %}
</PRE>
<HR>
</body>
</html>

Se trata de un ejemplo sencillo, no hay validación de imágenes duplicadas. Además seria posible enviar un código de control a través de la petición POST para evitar que nos suban imágenes de terceros o incluso usar el sistema de loging de Google.

Iker Tecnologia , , , , , , , , , , ,