thumbor-libs-blackhand 0.1.6__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- thumbor_libs_blackhand/loaders/__init__.py +2 -4
- thumbor_libs_blackhand/loaders/mongodb_loader.py +2 -4
- thumbor_libs_blackhand/loaders/spec_http_or_specb_loader.py +2 -3
- thumbor_libs_blackhand/loaders/specb_file_fallback_file_loader.py +2 -4
- thumbor_libs_blackhand/metrics/__init__.py +2 -4
- thumbor_libs_blackhand/mongodb/pool_result_storage.py +60 -0
- thumbor_libs_blackhand/result_storages/__init__.py +4 -0
- thumbor_libs_blackhand/result_storages/hybrid_result_storage.py +213 -0
- thumbor_libs_blackhand/result_storages/mongodb_result_storage_v3.py +196 -0
- thumbor_libs_blackhand/result_storages/mongodb_result_storage_v4.py +209 -0
- thumbor_libs_blackhand/result_storages/mongodb_result_storage_v5.py +178 -0
- thumbor_libs_blackhand/storages/__init__.py +4 -0
- thumbor_libs_blackhand/storages/mongodb_webp_storage.py +5 -12
- thumbor_libs_blackhand/url_signers/__init__.py +2 -4
- thumbor_libs_blackhand/url_signers/base64_hmac_sha1_trim.py +3 -3
- thumbor_libs_blackhand-0.5.0.dist-info/LICENSE +21 -0
- {thumbor_libs_blackhand-0.1.6.dist-info → thumbor_libs_blackhand-0.5.0.dist-info}/METADATA +7 -4
- thumbor_libs_blackhand-0.5.0.dist-info/RECORD +20 -0
- {thumbor_libs_blackhand-0.1.6.dist-info → thumbor_libs_blackhand-0.5.0.dist-info}/WHEEL +1 -1
- thumbor_libs_blackhand/result_storages/mongodb_webp_result_storage.py +0 -117
- thumbor_libs_blackhand-0.1.6.dist-info/LICENSE +0 -674
- thumbor_libs_blackhand-0.1.6.dist-info/RECORD +0 -16
- {thumbor_libs_blackhand-0.1.6.dist-info → thumbor_libs_blackhand-0.5.0.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
#!/usr/bin/python
|
|
2
1
|
# -*- coding: utf-8 -*-
|
|
3
2
|
# Blackhand library for Thumbor
|
|
4
|
-
# Licensed under the
|
|
5
|
-
#
|
|
3
|
+
# Licensed under the MIT license:
|
|
4
|
+
# http://www.opensource.org/licenses/mit-license
|
|
6
5
|
|
|
7
6
|
from pymongo.mongo_client import MongoClient
|
|
8
|
-
from pymongo.server_api import ServerApi
|
|
9
7
|
from bson.objectid import ObjectId
|
|
10
8
|
from thumbor.loaders import LoaderResult
|
|
11
9
|
import gridfs
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
#!/usr/bin/python
|
|
2
1
|
# -*- coding: utf-8 -*-
|
|
3
2
|
# Blackhand library for Thumbor
|
|
4
|
-
# Licensed under the
|
|
5
|
-
#
|
|
3
|
+
# Licensed under the MIT license:
|
|
4
|
+
# http://www.opensource.org/licenses/mit-license
|
|
6
5
|
|
|
7
6
|
from thumbor.loaders import http_loader
|
|
8
7
|
from thumbor_libs_blackhand.loaders import specb_file_fallback_file_loader
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
#!/usr/bin/python
|
|
2
1
|
# -*- coding: utf-8 -*-
|
|
3
2
|
# Blackhand library for Thumbor
|
|
4
|
-
# Licensed under the
|
|
5
|
-
#
|
|
3
|
+
# Licensed under the MIT license:
|
|
4
|
+
# http://www.opensource.org/licenses/mit-license
|
|
6
5
|
|
|
7
6
|
from os import fstat
|
|
8
7
|
from datetime import datetime
|
|
@@ -30,7 +29,6 @@ async def load(context, path):
|
|
|
30
29
|
result.error = LoaderResult.ERROR_NOT_FOUND
|
|
31
30
|
result.successful = False
|
|
32
31
|
return result
|
|
33
|
-
pass
|
|
34
32
|
|
|
35
33
|
if not exists(file_path):
|
|
36
34
|
file_path = unquote(file_path)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Licensed under the MIT license:
|
|
2
|
+
# http://www.opensource.org/licenses/mit-license
|
|
3
|
+
# Copyright (c) 2020 Eka Cahya Pratama <ekapratama93@gmail.com>
|
|
4
|
+
|
|
5
|
+
from motor.motor_tornado import MotorClient
|
|
6
|
+
from pymongo import ASCENDING, DESCENDING
|
|
7
|
+
from tornado.gen import convert_yielded
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Singleton(type):
|
|
11
|
+
"""
|
|
12
|
+
Define an Instance operation that lets clients access its unique
|
|
13
|
+
instance.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(cls, name, bases, attrs, **kwargs):
|
|
17
|
+
super().__init__(name, bases, attrs)
|
|
18
|
+
cls._instance = None
|
|
19
|
+
|
|
20
|
+
def __call__(cls, *args, **kwargs):
|
|
21
|
+
if cls._instance is None:
|
|
22
|
+
cls._instance = super().__call__(*args, **kwargs)
|
|
23
|
+
return cls._instance
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MongoConnector(metaclass=Singleton):
|
|
27
|
+
|
|
28
|
+
def __init__(self,
|
|
29
|
+
uri=None,
|
|
30
|
+
host=None,
|
|
31
|
+
port=None,
|
|
32
|
+
db_name=None,
|
|
33
|
+
col_name=None):
|
|
34
|
+
self.uri = uri
|
|
35
|
+
self.host = host
|
|
36
|
+
self.port = port
|
|
37
|
+
self.db_name = db_name
|
|
38
|
+
self.col_name = col_name
|
|
39
|
+
self.db_conn, self.col_conn = self.create_connection()
|
|
40
|
+
convert_yielded(self.ensure_index())
|
|
41
|
+
|
|
42
|
+
def create_connection(self):
|
|
43
|
+
if self.uri:
|
|
44
|
+
connection = MotorClient(self.uri)
|
|
45
|
+
else:
|
|
46
|
+
connection = MotorClient(self.host, self.port)
|
|
47
|
+
|
|
48
|
+
db_conn = connection[self.db_name]
|
|
49
|
+
col_conn = db_conn[self.col_name]
|
|
50
|
+
|
|
51
|
+
return db_conn, col_conn
|
|
52
|
+
|
|
53
|
+
async def ensure_index(self):
|
|
54
|
+
index_name = 'key_1_created_at_-1'
|
|
55
|
+
indexes = await self.col_conn.index_information()
|
|
56
|
+
if index_name not in indexes:
|
|
57
|
+
await self.col_conn.create_index(
|
|
58
|
+
[('path', ASCENDING), ('created_at', DESCENDING)],
|
|
59
|
+
name=index_name
|
|
60
|
+
)
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Blackhand library for Thumbor
|
|
3
|
+
# Licensed under the MIT license:
|
|
4
|
+
# http://www.opensource.org/licenses/mit-license
|
|
5
|
+
# libvips
|
|
6
|
+
|
|
7
|
+
from urllib import parse #, request; error
|
|
8
|
+
#from motor.motor_tornado import MotorGridFSBucket
|
|
9
|
+
#from pymongo.errors import PyMongoError
|
|
10
|
+
from thumbor_libs_blackhand.mongodb.pool_result_storage import MongoConnector
|
|
11
|
+
from datetime import datetime, timedelta
|
|
12
|
+
from thumbor.result_storages import BaseStorage, ResultStorageResult
|
|
13
|
+
from thumbor.engines import BaseEngine
|
|
14
|
+
from thumbor.utils import logger
|
|
15
|
+
from bson.binary import Binary
|
|
16
|
+
from uuid import uuid4
|
|
17
|
+
import pytz
|
|
18
|
+
import re
|
|
19
|
+
#import os
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Storage(BaseStorage):
|
|
23
|
+
|
|
24
|
+
def __init__(self, context):
|
|
25
|
+
BaseStorage.__init__(self, context)
|
|
26
|
+
self.database, self.storage = self.__conn__()
|
|
27
|
+
super(Storage, self).__init__(context)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def __conn__(self):
|
|
31
|
+
'''Return the MongoDB params.
|
|
32
|
+
:returns: MongoDB DB and Collection
|
|
33
|
+
:rtype: pymongo.database.Database, pymongo.database.Collection
|
|
34
|
+
'''
|
|
35
|
+
|
|
36
|
+
password = parse.quote_plus(self.context.config.MONGO_RESULT_STORAGE_SERVER_PASSWORD)
|
|
37
|
+
user = parse.quote_plus(self.context.config.MONGO_RESULT_STORAGE_SERVER_USER)
|
|
38
|
+
|
|
39
|
+
if not self.context.config.MONGO_RESULT_STORAGE_SERVER_REPLICASET:
|
|
40
|
+
uric = ('mongodb://'+ user +':' + password + '@' + self.context.config.MONGO_RESULT_STORAGE_SERVER_HOSTS
|
|
41
|
+
+ '/?authSource=' + self.context.config.MONGO_RESULT_STORAGE_SERVER_DB)
|
|
42
|
+
else:
|
|
43
|
+
uric = ('mongodb://'+ user +':' + password + '@' + self.context.config.MONGO_RESULT_STORAGE_SERVER_HOSTS
|
|
44
|
+
+ '/?authSource=' + self.context.config.MONGO_RESULT_STORAGE_SERVER_DB
|
|
45
|
+
+ "&replicaSet=" + self.context.config.MONGO_RESULT_STORAGE_SERVER_REPLICASET
|
|
46
|
+
+ "&readPreference=" + self.context.config.MONGO_RESULT_STORAGE_SERVER_READ_PREFERENCE)
|
|
47
|
+
|
|
48
|
+
db_name = self.context.config.MONGO_RESULT_STORAGE_SERVER_DB
|
|
49
|
+
col_name = self.context.config.MONGO_RESULT_STORAGE_SERVER_COLLECTION
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
uri = self.context.config.MONGO_RESULT_STORAGE_URI
|
|
53
|
+
except AttributeError:
|
|
54
|
+
uri=uric
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
host = self.context.config.MONGO_RESULT_STORAGE_SERVER_HOST
|
|
58
|
+
port = self.context.config.MONGO_RESULT_STORAGE_SERVER_PORT
|
|
59
|
+
except AttributeError:
|
|
60
|
+
host=None
|
|
61
|
+
port=None
|
|
62
|
+
|
|
63
|
+
mongo_conn = MongoConnector(
|
|
64
|
+
db_name=db_name,
|
|
65
|
+
col_name=col_name,
|
|
66
|
+
uri=uri,
|
|
67
|
+
host=host,
|
|
68
|
+
port=port,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
database = mongo_conn.db_conn
|
|
72
|
+
storage = mongo_conn.col_conn
|
|
73
|
+
|
|
74
|
+
return database, storage
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def is_auto_webp(self):
|
|
79
|
+
return self.context.config.AUTO_WEBP and self.context.request.accepts_webp
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def is_auto_webp(self):
|
|
83
|
+
'''Return if webp.
|
|
84
|
+
:return: If the file is a webp
|
|
85
|
+
:rettype: boolean
|
|
86
|
+
'''
|
|
87
|
+
|
|
88
|
+
return self.context.config.AUTO_WEBP \
|
|
89
|
+
and self.context.request.accepts_webp
|
|
90
|
+
|
|
91
|
+
def get_key_from_request(self):
|
|
92
|
+
'''Return a path key for the current request url.
|
|
93
|
+
:return: The storage key for the current url
|
|
94
|
+
:rettype: string
|
|
95
|
+
'''
|
|
96
|
+
|
|
97
|
+
path = f"result:{self.context.request.url}"
|
|
98
|
+
|
|
99
|
+
if self.is_auto_webp:
|
|
100
|
+
return f'{path}/webp'
|
|
101
|
+
|
|
102
|
+
return path
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_max_age(self):
|
|
106
|
+
|
|
107
|
+
return self.context.config.RESULT_STORAGE_EXPIRATION_SECONDS
|
|
108
|
+
|
|
109
|
+
async def put(self, image_bytes):
|
|
110
|
+
key = self.get_key_from_request()
|
|
111
|
+
#max_age = self.get_max_age()
|
|
112
|
+
#result_ttl = self.get_max_age()
|
|
113
|
+
try:
|
|
114
|
+
CACHE_PATH = self.context.config.CACHE_PATH
|
|
115
|
+
except AttributeError:
|
|
116
|
+
raise
|
|
117
|
+
imgpath = ( datetime.now().strftime('%Y') + '/'
|
|
118
|
+
+ datetime.now().strftime('%m') + '/' + datetime.now().strftime('%d')
|
|
119
|
+
+ '/' + datetime.now().strftime('%H'))
|
|
120
|
+
mkpath = (CACHE_PATH + '/' + imgpath)
|
|
121
|
+
self.ensure_dir(mkpath)
|
|
122
|
+
|
|
123
|
+
#os.makedirs(mkpath)
|
|
124
|
+
ref_img = ''
|
|
125
|
+
ref_img = re.findall(r'/[a-zA-Z0-9]{24}(?:$|/)', key)
|
|
126
|
+
if ref_img:
|
|
127
|
+
ref_img2 = ref_img[0].replace('/','')
|
|
128
|
+
else:
|
|
129
|
+
ref_img2 = 'undef'
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
if self.context.config.get("MONGO_STORE_METADATA", False):
|
|
133
|
+
metadata = dict(self.context.headers)
|
|
134
|
+
else:
|
|
135
|
+
metadata = {}
|
|
136
|
+
|
|
137
|
+
cache_id = str(uuid4())
|
|
138
|
+
endingpath= mkpath + "/" + cache_id
|
|
139
|
+
doc = {
|
|
140
|
+
'path': key,
|
|
141
|
+
'created_at': datetime.utcnow(),
|
|
142
|
+
'data': "",
|
|
143
|
+
'metadata': metadata,
|
|
144
|
+
'content_type': BaseEngine.get_mimetype(image_bytes),
|
|
145
|
+
'ref_id': ref_img2,
|
|
146
|
+
'content_length' : len(image_bytes),
|
|
147
|
+
'cache_path': imgpath,
|
|
148
|
+
'cache_id' : cache_id
|
|
149
|
+
}
|
|
150
|
+
doc_cpm = dict(doc)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
fichier = open(endingpath, "wb")
|
|
154
|
+
try:
|
|
155
|
+
fichier.write(Binary(image_bytes))
|
|
156
|
+
finally:
|
|
157
|
+
fichier.close()
|
|
158
|
+
|
|
159
|
+
await self.storage.insert_one(doc_cpm)
|
|
160
|
+
#return self.context.request.url
|
|
161
|
+
return key
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
async def get(self):
|
|
165
|
+
|
|
166
|
+
key = self.get_key_from_request()
|
|
167
|
+
logger.debug("[RESULT_STORAGE] image not found at %s", key)
|
|
168
|
+
try:
|
|
169
|
+
CACHE_PATH = self.context.config.CACHE_PATH
|
|
170
|
+
except AttributeError:
|
|
171
|
+
raise
|
|
172
|
+
age = datetime.utcnow() - timedelta(
|
|
173
|
+
seconds=self.get_max_age()
|
|
174
|
+
)
|
|
175
|
+
stored = await self.storage.find_one({
|
|
176
|
+
'path': key,
|
|
177
|
+
'created_at': {
|
|
178
|
+
'$gte': age
|
|
179
|
+
},
|
|
180
|
+
}, {
|
|
181
|
+
'ref_id': True,
|
|
182
|
+
'created_at': True,
|
|
183
|
+
'metadata': True,
|
|
184
|
+
'content_type': True,
|
|
185
|
+
'data' : True,
|
|
186
|
+
'content_length': True,
|
|
187
|
+
'cache_path' : True,
|
|
188
|
+
'cache_id' : True,
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
if not stored:
|
|
193
|
+
return None
|
|
194
|
+
metadata = stored['metadata']
|
|
195
|
+
metadata['LastModified'] = stored['created_at'].replace(
|
|
196
|
+
tzinfo=pytz.utc
|
|
197
|
+
)
|
|
198
|
+
metadata['Cache-Control'] = "max-age=60,public"
|
|
199
|
+
metadata['ContentLength'] = stored['content_length']
|
|
200
|
+
metadata['ContentType'] = stored['content_type']
|
|
201
|
+
cachefile = CACHE_PATH + '/' + stored['cache_path'] + "/" + stored['cache_id']
|
|
202
|
+
|
|
203
|
+
fichier = open(cachefile, "rb")
|
|
204
|
+
try:
|
|
205
|
+
tosend = fichier.read()
|
|
206
|
+
finally:
|
|
207
|
+
fichier.close()
|
|
208
|
+
|
|
209
|
+
return ResultStorageResult(
|
|
210
|
+
buffer=tosend,
|
|
211
|
+
metadata=metadata,
|
|
212
|
+
successful=True
|
|
213
|
+
)
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Blackhand library for Thumbor
|
|
3
|
+
# Licensed under the MIT license:
|
|
4
|
+
# http://www.opensource.org/licenses/mit-license
|
|
5
|
+
|
|
6
|
+
from urllib import parse #, request; error
|
|
7
|
+
#from motor.motor_tornado import MotorGridFSBucket
|
|
8
|
+
#from pymongo.errors import PyMongoError
|
|
9
|
+
from thumbor_libs_blackhand.mongodb.pool_result_storage import MongoConnector
|
|
10
|
+
from datetime import datetime, timedelta
|
|
11
|
+
from thumbor.result_storages import BaseStorage, ResultStorageResult
|
|
12
|
+
from thumbor.engines import BaseEngine
|
|
13
|
+
from thumbor.utils import logger
|
|
14
|
+
from bson.binary import Binary
|
|
15
|
+
import pytz
|
|
16
|
+
import re
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Storage(BaseStorage):
|
|
20
|
+
|
|
21
|
+
def __init__(self, context):
|
|
22
|
+
BaseStorage.__init__(self, context)
|
|
23
|
+
self.database, self.storage = self.__conn__()
|
|
24
|
+
super(Storage, self).__init__(context)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def __conn__(self):
|
|
29
|
+
'''Return the MongoDB params.
|
|
30
|
+
:returns: MongoDB DB and Collection
|
|
31
|
+
:rtype: pymongo.database.Database, pymongo.database.Collection
|
|
32
|
+
'''
|
|
33
|
+
|
|
34
|
+
password = parse.quote_plus(self.context.config.MONGO_RESULT_STORAGE_SERVER_PASSWORD)
|
|
35
|
+
user = parse.quote_plus(self.context.config.MONGO_RESULT_STORAGE_SERVER_USER)
|
|
36
|
+
if not self.context.config.MONGO_RESULT_STORAGE_SERVER_REPLICASET:
|
|
37
|
+
uric = ('mongodb://'+ user +':' + password + '@' + self.context.config.MONGO_RESULT_STORAGE_SERVER_HOSTS
|
|
38
|
+
+ '/?authSource=' + self.context.config.MONGO_RESULT_STORAGE_SERVER_DB)
|
|
39
|
+
else:
|
|
40
|
+
uric = ('mongodb://'+ user +':' + password + '@' + self.context.config.MONGO_RESULT_STORAGE_SERVER_HOSTS
|
|
41
|
+
+ '/?authSource=' + self.context.config.MONGO_RESULT_STORAGE_SERVER_DB
|
|
42
|
+
+ "&replicaSet=" + self.context.config.MONGO_RESULT_STORAGE_SERVER_REPLICASET
|
|
43
|
+
+ "&readPreference=" + self.context.config.MONGO_RESULT_STORAGE_SERVER_READ_PREFERENCE)
|
|
44
|
+
|
|
45
|
+
db_name = self.context.config.MONGO_RESULT_STORAGE_SERVER_DB
|
|
46
|
+
col_name = self.context.config.MONGO_RESULT_STORAGE_SERVER_COLLECTION
|
|
47
|
+
host = None
|
|
48
|
+
port = None
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
uri = self.context.config.MONGO_RESULT_STORAGE_URI
|
|
52
|
+
except AttributeError:
|
|
53
|
+
uri=uric
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
host = self.context.config.MONGO_RESULT_STORAGE_SERVER_HOST
|
|
57
|
+
port = self.context.config.MONGO_RESULT_STORAGE_SERVER_PORT
|
|
58
|
+
except AttributeError:
|
|
59
|
+
host=None
|
|
60
|
+
port=None
|
|
61
|
+
|
|
62
|
+
mongo_conn = MongoConnector(
|
|
63
|
+
db_name=db_name,
|
|
64
|
+
col_name=col_name,
|
|
65
|
+
uri=uri,
|
|
66
|
+
host=host,
|
|
67
|
+
port=port,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
database = mongo_conn.db_conn
|
|
71
|
+
storage = mongo_conn.col_conn
|
|
72
|
+
|
|
73
|
+
return database, storage
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def is_auto_webp(self):
|
|
77
|
+
'''Return if webp.
|
|
78
|
+
:return: If the file is a webp
|
|
79
|
+
:rettype: boolean
|
|
80
|
+
'''
|
|
81
|
+
|
|
82
|
+
return self.context.config.AUTO_WEBP \
|
|
83
|
+
and self.context.request.accepts_webp
|
|
84
|
+
|
|
85
|
+
def get_key_from_request(self):
|
|
86
|
+
'''Return a path key for the current request url.
|
|
87
|
+
:return: The storage key for the current url
|
|
88
|
+
:rettype: string
|
|
89
|
+
'''
|
|
90
|
+
|
|
91
|
+
path = f"result:{self.context.request.url}"
|
|
92
|
+
|
|
93
|
+
if self.is_auto_webp:
|
|
94
|
+
return f'{path}/webp'
|
|
95
|
+
|
|
96
|
+
return path
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_max_age(self):
|
|
100
|
+
|
|
101
|
+
return self.context.config.RESULT_STORAGE_EXPIRATION_SECONDS
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
async def put(self, image_bytes):
|
|
105
|
+
key = self.get_key_from_request()
|
|
106
|
+
#max_age = self.get_max_age()
|
|
107
|
+
#result_ttl = self.get_max_age()
|
|
108
|
+
ref_img = ''
|
|
109
|
+
ref_img = re.findall(r'/[a-zA-Z0-9]{24}(?:$|/)', key)
|
|
110
|
+
if ref_img:
|
|
111
|
+
ref_img2 = ref_img[0].replace('/','')
|
|
112
|
+
else:
|
|
113
|
+
ref_img2 = 'undef'
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if self.context.config.get("MONGO_STORE_METADATA", False):
|
|
117
|
+
metadata = dict(self.context.headers)
|
|
118
|
+
else:
|
|
119
|
+
metadata = {}
|
|
120
|
+
|
|
121
|
+
doc = {
|
|
122
|
+
'path': key,
|
|
123
|
+
'created_at': datetime.utcnow(),
|
|
124
|
+
'data': Binary(image_bytes),
|
|
125
|
+
'metadata': metadata,
|
|
126
|
+
'content_type': BaseEngine.get_mimetype(image_bytes),
|
|
127
|
+
'ref_id': ref_img2,
|
|
128
|
+
'content_length' : len(image_bytes)
|
|
129
|
+
}
|
|
130
|
+
doc_cpm = dict(doc)
|
|
131
|
+
|
|
132
|
+
await self.storage.insert_one(doc_cpm)
|
|
133
|
+
#return self.context.request.url
|
|
134
|
+
return key
|
|
135
|
+
|
|
136
|
+
async def get(self):
|
|
137
|
+
key = self.get_key_from_request()
|
|
138
|
+
logger.debug("[RESULT_STORAGE] image not found at %s", key)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
age = datetime.utcnow() - timedelta(
|
|
142
|
+
seconds=self.get_max_age()
|
|
143
|
+
)
|
|
144
|
+
stored = await self.storage.find_one({
|
|
145
|
+
'path': key,
|
|
146
|
+
'created_at': {
|
|
147
|
+
'$gte': age
|
|
148
|
+
},
|
|
149
|
+
}, {
|
|
150
|
+
'ref_id': True,
|
|
151
|
+
'created_at': True,
|
|
152
|
+
'metadata': True,
|
|
153
|
+
'content_type': True,
|
|
154
|
+
'data' : True,
|
|
155
|
+
'content_length': True,
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
if not stored:
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
#self.context.config.MONGO_RESULT_STORAGE_MAXCACHESIZE
|
|
164
|
+
dedup = self.context.config.MONGO_RESULT_STORAGE_DEDUP
|
|
165
|
+
except:
|
|
166
|
+
dedup = False
|
|
167
|
+
|
|
168
|
+
if not dedup:
|
|
169
|
+
logger.debug("Deduplication OFF")
|
|
170
|
+
else:
|
|
171
|
+
filter={
|
|
172
|
+
'path': key
|
|
173
|
+
}
|
|
174
|
+
sort=list({
|
|
175
|
+
'created_at': -1
|
|
176
|
+
}.items())
|
|
177
|
+
skip=1
|
|
178
|
+
obj = self.storage.find(filter=filter, skip=skip)
|
|
179
|
+
async for doc in obj:
|
|
180
|
+
logger.info("Deduplication %s", key)
|
|
181
|
+
self.storage.delete_one({"_id": doc["_id"]})
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
metadata = stored['metadata']
|
|
185
|
+
metadata['LastModified'] = stored['created_at'].replace(
|
|
186
|
+
tzinfo=pytz.utc
|
|
187
|
+
)
|
|
188
|
+
metadata['Cache-Control'] = "max-age=60,public"
|
|
189
|
+
metadata['ContentLength'] = stored['content_length']
|
|
190
|
+
metadata['ContentType'] = stored['content_type']
|
|
191
|
+
|
|
192
|
+
return ResultStorageResult(
|
|
193
|
+
buffer=stored['data'],
|
|
194
|
+
metadata=metadata,
|
|
195
|
+
successful=True
|
|
196
|
+
)
|