如何修改web2py下载功能以在文件未更改时返回“304未修改”?

1 投票
2 回答
1309 浏览
提问于 2025-04-17 13:37

在我的情况中,我有一个数据库,里面存储了文档的缩略图,这些缩略图会在一个概览页面上显示。每次浏览器访问这个网站时,所有的缩略图都会重新下载,这样下载功能就会再次从数据库加载所有的图片。因为我是在GAE上运行,这样会导致我的数据库读取量大幅增加。

我尝试通过设置来启用客户端缓存:

@auth.requires_login()
def download():
  response.headers['Cache-Control'] = None
  response.headers['Pragma'] = None
  response.headers['Expires'] = None
  return response.download(request, db)

我还听说过response.stream可能会有帮助,但web2py的书上说:

如上所述,response.download应该用于获取通过上传字段存储的文件。response.stream可以用于其他情况,比如返回一个临时文件或由控制器创建的StringIO对象。

-- 编辑 --

我通过以下方式启用了客户端缓存:

session.forget() #important
expire_time = datetime.timedelta(days=2)

response.headers['Cache-Control'] = 'private, max-age%d'%(60*60*24*2)
response.headers['Pragma'] = None
response.headers['Expires'] = (request.utcnow + expire_time).strftime("%a, %d %b %Y %H:%M:%S GMT")
response.headers['Content-Disposition'] = \
      'attachment;filename=' + filename + ';'

return response.stream(stream, filename=filename)

2 个回答

0

如果你需要提供图片,为什么不试试谷歌的高性能图片服务呢?使用这个API(get_serving_url),谷歌会帮你提供图片。你不需要自己处理!这个服务很快,你还可以调整图片大小,唯一需要注意的是,它会消耗一些带宽。

不过,这个服务只适用于图片。下面是我用来从blobstore提供CSS的一些代码,包括304状态码的处理。

class DynCSS(blobstore_handlers.BlobstoreDownloadHandler):

    def get(self, resource):                                        

        (key, _, _) = resource.rpartition('.')
        self.response.headers[str('ETag')] = str(key)
        if 'If-None-Match' in self.request.headers:
            etags = [x.strip()
                     for x in self.request.headers[str('If-None-Match')].split(',')]
            if key in etags:
                self.response.set_status(304) # optimize traffic 304
                return
        blob_info = blobstore.BlobInfo.get(key) 
        self.send_blob(blob_info, save_as=True)

这里的资源参数是一个带有.css后缀的blob_key。我还使用了unicode字面量。

高性能图片服务使用的是:get_serving_url https://developers.google.com/appengine/docs/python/images/functions

举个例子:

entity.serving_url = images.get_serving_url(entity.blob_key, size=None, secure_url=True)

使用方法:

  • 把你的图片上传到blobstore
  • 在数据存储中保存blob_key和你的图片文件名
  • 创建一个get_serving_url。这个只需要创建一次。
  • 在你的模板的img标签中使用这个serving url。

一个serving_url看起来像这样:https://lh6.ggpht.com/lOghqU2JrYk8M-Aoio8WjMM6mstgZcTP0VzJk79HteVLhnwZy0kqbgVGQZYP8YsoqVNzsu0EBysX16qMJe7H2BsOAr4j=s70

2

首先,如果这些图片是公开的,不需要登录就能查看,那么你可以把它们放在/static文件夹里,而不是/uploads文件夹。这样的话,你可以直接把它们当作静态文件来提供。这样做的好处是,浏览器会自动设置一些头信息来帮助缓存,这样加载会更快,也更有效率。

其次,你可以使用response.stream()来流式传输上传的文件,但它不会自动找到文件所在的文件夹,也不会从编码后的文件名中解码出原始文件名并把它加到Content-Disposition头里(不过在这种情况下你其实不需要这个,因为你只是要显示图片,而不是下载它们)。所以,只要你把完整的文件路径传给response.stream(),在这种情况下你就可以使用它,它会为缓存设置合适的响应头。

最后,如果你想直接设置响应头,可以这样做:

import os
import time
modified = os.stat(file)[stat.ST_MTIME]
mtime = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(modified))
response.headers['Last-Modified'] = mtime
response.headers['Pragma'] = 'cache'
response.headers['Cache-Control'] = 'private'

撰写回答