如何缓存返回图片的Flask视图?序列化pymongo文件响应遇阻
Flask-Caching缓存MongoDB流式Response报错:无法序列化_thread.lock对象
问题原因
你通过app.mongo.send_file返回的是流式Flask Response,这类Response内部关联了MongoDB文件流的底层资源(比如游标或连接的线程锁),而Flask-Caching默认使用的pickle序列化器无法处理_thread.lock这类线程同步对象,因此缓存时抛出TypeError。
普通Response(比如直接返回字符串/bytes)没有绑定这类线程锁资源,所以能正常被缓存。
解决方案
方案1:读取文件内容到内存,生成静态Response后缓存
适合小文件场景,将流式内容转为静态bytes,避免锁对象被序列化:
from flask import make_response @app.route("/media/<collection>/<unique_path>/<path:filename>") @cache.cached() def view_media_file(collection, unique_path, filename): document = db.find_one_or_404(collection, 'unique_path', unique_path) hex_id = filename.rsplit(".", 1)[0] if hex_id not in document.get("files", {}).keys(): return "Error", 404 # 直接从GridFS读取文件内容到内存 fs = app.mongo.cx[db.name].fs gridfs_file = fs.find_one({"filename": filename}) file_data = gridfs_file.read() # 构建静态Response response = make_response(file_data) # 复制原Response的headers(或按需自定义) response.headers["Content-Type"] = gridfs_file.content_type response.headers["Cache-Control"] = "max-age=31536000, public" return response
方案2:自定义缓存序列化器,只缓存Response的可序列化部分
通过自定义序列化逻辑,提取Response的状态码、headers、内容进行缓存,避开不可序列化的锁对象:
from flask import make_response, Cache # 自定义序列化:提取Response的可序列化字段 def serialize_response(response): return { "status_code": response.status_code, "headers": dict(response.headers), "data": response.get_data() # 读取流式内容为bytes } # 自定义反序列化:从缓存数据重建Response def deserialize_response(cached_data): response = make_response(cached_data["data"]) response.status_code = cached_data["status_code"] response.headers.update(cached_data["headers"]) return response # 初始化缓存时指定自定义序列化器 cache = Cache(app, config={ "CACHE_TYPE": "SimpleCache", # 替换为你实际使用的缓存类型(如RedisCache) "CACHE_SERIALIZER": { "dumps": serialize_response, "loads": deserialize_response } }) @app.route("/media/<collection>/<unique_path>/<path:filename>") @cache.cached() def view_media_file(collection, unique_path, filename): # 原逻辑保持不变 document = db.find_one_or_404(collection, 'unique_path', unique_path) hex_id = filename.rsplit(".", 1)[0] if not hex_id in document.get("files", {}).keys(): return "Error", 404 response_obj = app.mongo.send_file(filename) return response_obj
方案3:用HTTP缓存替代服务器端缓存(推荐大文件场景)
如果是大文件,服务器端缓存会占用大量内存,更高效的方式是通过HTTP缓存头让客户端/CDN缓存,完全避开序列化问题:
@app.route("/media/<collection>/<unique_path>/<path:filename>") def view_media_file(collection, unique_path, filename): document = db.find_one_or_404(collection, 'unique_path', unique_path) hex_id = filename.rsplit(".", 1)[0] if not hex_id in document.get("files", {}).keys(): return "Error", 404 response_obj = app.mongo.send_file(filename) # 设置强缓存:客户端直接缓存1年 response_obj.headers["Cache-Control"] = "public, max-age=31536000, immutable" # 设置协商缓存:用文件哈希作为ETag,文件不变则返回304 response_obj.set_etag(document["files"][hex_id]["file_hash"]) # 假设你的document存储了文件哈希字段 return response_obj
内容的提问来源于stack exchange,提问作者scoofy
相关产品推荐
相关产品推荐

