Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Performance

  • Loading branch information...
commit 488c41aceec26fb7c6d75d0679e9bf8448032c8c 1 parent 160d1f8
@heynemann heynemann authored
View
2  Makefile
@@ -1,5 +1,5 @@
run:
- @PYTHONPATH=.:$$PYTHONPATH python thumbor/server.py -l debug
+ @PYTHONPATH=.:$$PYTHONPATH python thumbor/server.py -l error
pretest:
@-mysql -u root -e 'DROP DATABASE IF EXISTS thumbor_tests'
View
6 thumbor/app.py
@@ -16,7 +16,7 @@
from tornado.options import parse_config_file
from thumbor.config import conf
-from thumbor.handlers.unsafe import MainHandler
+from thumbor.handlers.unsafe import MainHandler, ImagingHandler
from thumbor.handlers.healthcheck import HealthcheckHandler
from thumbor.handlers.crypto import CryptoHandler
from thumbor.utils import real_import, logger
@@ -54,12 +54,12 @@ def __init__(self, conf_file=None, custom_handlers=None):
}
handlers = [
- (r'/healthcheck', HealthcheckHandler)
+ (r'/healthcheck', HealthcheckHandler),
]
if conf.ALLOW_UNSAFE_URL:
handlers.append(
- (Url.regex(), MainHandler, handler_context),
+ (Url.regex(), ImagingHandler, handler_context)
)
if custom_handlers:
View
10 thumbor/engines/graphicsmagick.py
@@ -14,10 +14,10 @@
from thumbor.engines import BaseEngine
FORMATS = {
- '.jpg': 'JPEG',
- '.jpeg': 'JPEG',
- '.gif': 'RGBA',
- '.png': 'PNG'
+ 'jpg': 'JPEG',
+ 'jpeg': 'JPEG',
+ 'gif': 'RGBA',
+ 'png': 'PNG'
}
class Engine(BaseEngine):
@@ -65,7 +65,7 @@ def read(self, extension=None, quality=options.QUALITY):
#'HermiteFilter', 'LanczosFilter', 'MitchellFilter',
#'PointFilter', 'QuadraticFilter', 'SincFilter', 'TriangleFilter']
- f = FilterTypes.CatromFilter
+ f = FilterTypes.CubicFilter
self.image.filterType(f)
View
10 thumbor/engines/imagemagick.py
@@ -17,10 +17,10 @@
from thumbor.vendor.pythonmagickwand import wand
FORMATS = {
- '.jpg': 'JPEG',
- '.jpeg': 'JPEG',
- '.gif': 'GIF',
- '.png': 'PNG'
+ 'jpg': 'JPEG',
+ 'jpeg': 'JPEG',
+ 'gif': 'GIF',
+ 'png': 'PNG'
}
class Engine(BaseEngine):
@@ -29,7 +29,7 @@ def reset_image(self):
self.load(self.read(), self.extension)
def create_image(self, buffer):
- return Image(StringIO(buffer))
+ return Image(buffer=buffer)
def resize(self, width, height):
self.image.resize((int(width), int(height)), wand.CATROM_FILTER, 1)
View
8 thumbor/engines/opencv.py
@@ -16,10 +16,10 @@
from thumbor.engines import BaseEngine
FORMATS = {
- '.jpg': 'JPEG',
- '.jpeg': 'JPEG',
- '.gif': 'GIF',
- '.png': 'PNG'
+ 'jpg': 'JPEG',
+ 'jpeg': 'JPEG',
+ 'gif': 'GIF',
+ 'png': 'PNG'
}
class Engine(BaseEngine):
View
8 thumbor/engines/pil.py
@@ -16,10 +16,10 @@
from thumbor.engines import BaseEngine
FORMATS = {
- '.jpg': 'JPEG',
- '.jpeg': 'JPEG',
- '.gif': 'GIF',
- '.png': 'PNG'
+ 'jpg': 'JPEG',
+ 'jpeg': 'JPEG',
+ 'gif': 'GIF',
+ 'png': 'PNG'
}
class Engine(BaseEngine):
View
94 thumbor/handlers/unsafe.py
@@ -8,10 +8,104 @@
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
+from os.path import splitext
+
import tornado.web
+from tornado.options import options
from thumbor.handlers import ContextHandler
+int_or_0 = lambda value: 0 if value is None else int(value)
+
+class Context(dict):
+ @classmethod
+ def from_dict(cls, kw):
+ opt = cls()
+
+ opt['meta'] = kw['meta'] == 'meta'
+
+ opt['crop'] = {
+ 'left': int_or_0(kw['crop_left']),
+ 'top': int_or_0(kw['crop_top']),
+ 'right': int_or_0(kw['crop_right']),
+ 'bottom': int_or_0(kw['crop_bottom'])
+ }
+
+ opt['crop']['enabled'] = opt['crop']['left'] > 0 or \
+ opt['crop']['top'] > 0 or \
+ opt['crop']['right'] > 0 or \
+ opt['crop']['bottom'] > 0
+
+ opt['fit_in'] = kw['fit_in'] == 'fit-in'
+
+ opt['width'] = int_or_0(kw['width'])
+ opt['height'] = int_or_0(kw['height'])
+
+ if options.MAX_WIDTH and opt['width'] > options.MAX_WIDTH:
+ opt['width'] = options.MAX_WIDTH
+ if options.MAX_HEIGHT and opt['height'] > options.MAX_HEIGHT:
+ opt['height'] = options.MAX_HEIGHT
+
+ opt['horizontal_flip'] = kw['horizontal_flip'] == '-'
+ opt['vertical_flip'] = kw['vertical_flip'] == '-'
+
+ opt['halign'] = kw['halign'] or 'center'
+ opt['valign'] = kw['valign'] or 'middle'
+
+ opt['smart'] = kw['smart'] == 'smart'
+
+ opt['imageUrl'] = kw['image']
+ opt['extension'] = splitext(opt['imageUrl'])[-1].lower().lstrip('.')
+
+ return opt
+
+class ImagingHandler(ContextHandler):
+
+ @tornado.web.asynchronous
+ def get(self, **kw):
+ context = Context.from_dict(kw)
+
+ self.process_image(context)
+
+ def process_image(self, context):
+ def callback(buffer):
+ self.set_header('Content-Type', 'image/%s' % context['extension'])
+
+ self.engine.load(buffer, context['extension'])
+
+ #self.engine.resize(context['width'], context['height'])
+
+ #contents = self.engine.read()
+ self.write(self.engine.read())
+ #self.write(buffer)
+ #self.write('ok')
+ self.finish()
+
+ self.fetch_image(context, callback)
+
+ def fetch_image(self, context, callback):
+ storage = self.storage()
+ buffer = storage.get(context['imageUrl'])
+
+ if buffer is not None:
+ callback(buffer)
+ else:
+ def handle_loader_loaded(buffer):
+ if buffer is None:
+ callback(None)
+ return
+
+ #self.engine.load(buffer, context['extension'])
+ #self.engine.normalize()
+ #buffer = self.engine.read()
+
+ storage.put(context['imageUrl'], buffer)
+ storage.put_crypto(context['imageUrl'])
+
+ callback(buffer)
+
+ self.loader.load(context['imageUrl'], handle_loader_loaded)
+
class MainHandler(ContextHandler):
@tornado.web.asynchronous
View
52 thumbor/storages/in_memory_storage.py
@@ -0,0 +1,52 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# thumbor imaging service
+# https://github.com/globocom/thumbor/wiki
+
+# Licensed under the MIT license:
+# http://www.opensource.org/licenses/mit-license
+# Copyright (c) 2011 globo.com timehome@corp.globo.com
+
+from datetime import datetime, timedelta
+
+from thumbor.storages import BaseStorage
+from thumbor.config import conf
+
+class Storage(BaseStorage):
+
+ def __init__(self):
+ self.items = {}
+ self.crypto = {}
+ self.expires = {}
+
+ def __key_for(self, url):
+ return 'crypto-%s' % url
+
+ def put(self, path, bytes):
+ self.items[path] = bytes
+ self.expires[path] = datetime.now() + timedelta(seconds=conf.STORAGE_EXPIRATION_SECONDS)
+
+ def put_crypto(self, path):
+ if not conf.STORES_CRYPTO_KEY_FOR_EACH_IMAGE:
+ return
+
+ if not conf.SECURITY_KEY:
+ raise RuntimeError("STORES_CRYPTO_KEY_FOR_EACH_IMAGE can't be True if no SECURITY_KEY specified")
+
+ self.crypto[self.__key_for(path)] = conf.SECURITY_KEY
+
+ def get_crypto(self, path):
+ if not conf.STORES_CRYPTO_KEY_FOR_EACH_IMAGE:
+ return None
+
+ crypto = self.crypto.get(self.__key_for(path), None)
+
+ if not crypto:
+ return None
+ return crypto
+
+ def get(self, path):
+ item = self.items.get(path, None)
+ return item
+
View
11 thumbor/thumbor.conf
@@ -41,6 +41,7 @@ MAX_SOURCE_SIZE = 0
# how to store the original images
STORAGE = 'thumbor.storages.file_storage'
+#STORAGE = 'thumbor.storages.redis_storage'
# root path of the file storage
FILE_STORAGE_ROOT_PATH = '/tmp/thumbor/storage'
@@ -49,15 +50,9 @@ FILE_STORAGE_ROOT_PATH = '/tmp/thumbor/storage'
# this is VERY useful to allow changing the security key
STORES_CRYPTO_KEY_FOR_EACH_IMAGE = True
-#REDIS_STORAGE_SERVER = {
-# 'port': 6379,
-# 'host': 'localhost',
-# 'db': 0
-#})
-
# imaging engine to use to process images
-#ENGINE = 'thumbor.engines.graphicsmagick'
-ENGINE = 'thumbor.engines.pil'
+ENGINE = 'thumbor.engines.graphicsmagick'
+#ENGINE = 'thumbor.engines.pil'
#ENGINE = 'thumbor.engines.imagemagick'
#ENGINE = 'thumbor.engines.opencv'
View
6 thumbor/vendor/pythonmagickwand/image.py
@@ -16,13 +16,15 @@
class Image(object):
''' Represents a single image, supported by a MagickWand.'''
- def __init__(self, image=None, type=None):
+ def __init__(self, image=None, type=None, buffer=None):
self._wand = api.NewMagickWand()
if type:
api.MagickSetFilename(self._wand, 'buffer.%s' % type) # hint image type
- if hasattr(image, 'read'):
+ if buffer:
+ self._check_wand_error(api.MagickReadImageBlob(self._wand, buffer, len(buffer)))
+ elif hasattr(image, 'read'):
c = image.read()
self._check_wand_error(api.MagickReadImageBlob(self._wand, c, len(c)))
elif image:
Please sign in to comment.
Something went wrong with that request. Please try again.