# coding=utf-8 import os import tempfile import time import pickle from datetime import datetime try: from typing import Union, Any except: pass EXPIRE_NOW = 0 EXPIRE_1MIN = 60 EXPIRE_5MIN = EXPIRE_1MIN * 5 EXPIRE_30MIN = EXPIRE_1MIN * 30 EXPIRE_1HR = EXPIRE_1MIN * 60 EXPIRE_2HR = EXPIRE_1HR * 2 EXPIRE_3HR = EXPIRE_1HR * 3 EXPIRE_4HR = EXPIRE_1HR * 4 EXPIRE_6HR = EXPIRE_1HR * 6 EXPIRE_12HR = EXPIRE_1HR * 12 EXPIRE_1DAY = EXPIRE_1HR * 24 EXPIRE_2DAY = EXPIRE_1DAY * 2 EXPIRE_4DAY = EXPIRE_1DAY * 4 EXPIRE_1WEEK = EXPIRE_1DAY * 7 EXPIRE_1MOUTH = EXPIRE_1DAY * 31 EXPIRE_1YR = EXPIRE_1DAY * 365 DEFAULT_EXPIRE = EXPIRE_5MIN mime_expire_list = { 'application/javascript': EXPIRE_2DAY, 'application/x-javascript': EXPIRE_2DAY, 'text/javascript': EXPIRE_2DAY, 'text/css': EXPIRE_2DAY, 'text/x-cross-domain-policy': EXPIRE_2DAY, 'application/vnd.ms-fontobject': EXPIRE_4DAY, 'font/eot': EXPIRE_4DAY, 'font/opentype': EXPIRE_4DAY, 'application/x-font-ttf': EXPIRE_4DAY, 'application/font-woff': EXPIRE_4DAY, 'application/x-font-woff': EXPIRE_4DAY, 'font/woff': EXPIRE_4DAY, 'application/font-woff2': EXPIRE_4DAY, 'audio/ogg': EXPIRE_1HR, 'image/bmp': EXPIRE_1HR, 'image/gif': EXPIRE_12HR, 'image/jpeg': EXPIRE_12HR, 'image/png': EXPIRE_12HR, 'image/svg+xml': EXPIRE_12HR, 'image/webp': EXPIRE_12HR, 'video/mp4': EXPIRE_1HR, 'video/ogg': EXPIRE_1HR, 'video/webm': EXPIRE_1HR, 'image/vnd.microsoft.icon': EXPIRE_12HR, 'image/x-icon': EXPIRE_12HR, 'application/manifest+json': EXPIRE_12HR, 'application/atom+xml': EXPIRE_NOW, 'application/rss+xml': EXPIRE_NOW, 'application/json': EXPIRE_NOW, 'application/ld+json': EXPIRE_NOW, 'application/schema+json': EXPIRE_NOW, 'application/vnd.geo+json': EXPIRE_NOW, 'application/xml': EXPIRE_NOW, 'text/xml': EXPIRE_NOW, 'text/html': EXPIRE_NOW, 'application/x-web-client-manifest+json': EXPIRE_NOW, 'text/cache-manifest': EXPIRE_NOW, } def get_expire_from_mime(mime): return mime_expire_list.get(mime, EXPIRE_NOW) def _time_str_to_unix(timestring): """ :type timestring: Union[str, int] :rtype: Union[int, None] """ if isinstance(timestring, (int, float)): return timestring try: t = int(time.mktime(datetime.strptime(timestring, '%a, %d %b %Y %H:%M:%S %Z').timetuple())) except: t = None return t class FileCache: def __init__(self, max_size_kb=8192): self.items_dict = {} self.max_size_byte = max_size_kb * 1024 def __del__(self): self.flush_all() def put_obj(self, key, obj, expires=DEFAULT_EXPIRE, obj_size=0, last_modified=None, info_dict=None): """ 将一个对象存入缓存 :param key: key :param last_modified: str format: "Mon, 18 Nov 2013 09:02:42 GMT" :param obj_size: too big object should not be cached :param expires: seconds to expire :param info_dict: custom dict contains information, stored in memory, so can access quickly :type key: str :type last_modified: str :type info_dict: dict or None :type obj: Any """ if expires <= 0 or obj_size > self.max_size_byte: return False self.delete(key) temp_file = tempfile.NamedTemporaryFile(prefix="zmirror_", suffix=".tmp", delete=False) pickle.dump(obj, temp_file, protocol=pickle.HIGHEST_PROTOCOL) cache_item = ( temp_file.name, # 0 cache file path info_dict, # 1 custom dict contains information int(time.time()), # 2 added time (unix time) expires, # 3 expires second _time_str_to_unix(last_modified), # 4 last modified, unix time ) temp_file.close() self.items_dict[key] = cache_item return True def delete(self, key): if self._is_item_exist(key): file_path = self.items_dict[key][0] del self.items_dict[key] if os.path.exists(file_path): os.remove(file_path) def flush_all(self): for key in list(self.items_dict.keys()): self.delete(key) def check_all_expire(self, force_flush_all=False): if force_flush_all: self.flush_all() return keys_to_delete = [] for item_key in self.items_dict: if self.is_expires(item_key): keys_to_delete.append(item_key) for key in keys_to_delete: self.delete(key) def is_cached(self, key): if not self._is_item_exist(key): return False if self.is_expires(key): self.delete(key) return False else: return True def get_obj(self, key): if self.is_cached(key): file_path = self.items_dict[key][0] try: with open(file_path, "rb") as fp: obj = pickle.load(fp) except: self.delete(key) return None else: return obj else: return None def get_info(self, key): if self.is_cached(key): return self.items_dict[key][1] else: return None def is_unchanged(self, key, last_modified=None): if not self.is_cached(key) or last_modified is None: return False else: ct = self.items_dict[key][4] if ct is None: return False elif ct == _time_str_to_unix(last_modified): return True def is_expires(self, key): item = self.items_dict[key] if time.time() > item[2] + item[3]: return True return False def _is_item_exist(self, key): return key in self.items_dict