只有在已更改的情况下使用版本化资源为HTTP客户端提供服务 - 使用Flask

时间:2014-05-06 07:00:17

标签: python flask download

我正在运行一个基于Flask的网络服务器,它服务于一个版本化的资源(例如某个版本化程序的安装文件)。我想用新资源为我的HTTP客户端服务,以防它已经没有当前版本。如果有新版本,我希望客户端下载资源并安装它。

我的Flask服务器看起来像这样

import json
import redis
import math
import requests
from flask import Flask,render_template,request

app=Flask(__name__)

@app.route('/version', methods=['GET','POST'])
def getversion():

    r_server=redis.Redis("127.0.0.1")

    if request.method == 'POST':
        jsonobj_recieve=request.data
        data=json.loads(jsonobj)
        currentversion=r_server.hget('version')

        if data == currentversion:
            #code to return a 'ok'
        else:
            #code to return 'not ok' also should send the updated file to the client
    else:
        return r_server.hget('version')

if __name__ == '__main__':
    app.run(
        debug=True,
        host="127.0.0.1",
        port=80
    )

我的客户非常基本:

import sys
import json
import requests

url="http://127.0.0.1/version"
jsonobj=json.dumps(str(sys.argv[1]))

print jsonobj

r=requests.post(url,data=jsonobj)

我可能需要重新编码整个客户端,这不是问题,但我真的不知道从哪里开始....

2 个答案:

答案 0 :(得分:2)

要求审核

  • 拥有网络应用,提供版本化资源。它可以是例如带有应用程序的文件。
  • 拥有客户端,允许仅在以下情况下获取资源,服务器上的资源版本以及本地已有的客户端不同
  • 客户端知道资源的版本字符串
  • 允许客户端在新版本可用时学习新版本字符串

HTTP就像解决方案的设计

如果您想仅允许下载应用程序以防万一,客户端还没有它,可以使用以下设计:

  • 使用etag标头。这通常包含一些字符串,用于描述您希望从该URL获取的资源的唯一状态。在您的情况下,它可能是您的应用程序的当前版本号。
  • 在您的请求中,使用标题" if-none-match",提供您的应用程序在客户端的版本号。这将导致HTTP状态代码306 - Not Modified,以防您的客户端和服务器共享相同版本的资源。如果它不同,您只需提供资源的内容并使用它。您的资源还应在etag当前版本的资源中表示,您的客户应注意它,或从其他来源(例如从下载的文件中)找到新的版本名称。

此设计遵循HTTP原则。

Flask服务资源,在etag

中声明版本

这是专注于展示原则,你应该详细说明提供资源的真实内容。

from flask import Flask, Response, request
import werkzeug.exceptions

app = Flask(__name__)

class NotModified(werkzeug.exceptions.HTTPException):
    code = 304
    def get_response(self, environment):
        return Response(status=304)

@app.route('/download/app')
def downloadapp():
    currver = "1.0"
    if request.if_none_match and currver in request.if_none_match:
        raise NotModified
    def generate():
        yield "app_file_part 1"
        yield "app_file_part 2"
        yield "app_file_part 3"
    return Response(generate(), headers={"etag": currver})


if __name__ == '__main__':
    app.run(debug=True)

客户端仅获取资源(如果是新的

import requests

ver = "1.0"
url = "http://localhost:5000/download/app"

req = requests.get(url, headers={"If-None-Match": ver})

if req.status_code == 200:
    print "new content of resource", req.content
    new_ver = req.headers["etag"]
else:
    print "resource did not change since last time"

使用Web服务器(例如NGINX)的Web部件的替代解决方案

假设资源是静态文件,只在某个时间更新,您应该可以配置您的Web服务器,例如NGINX,用于提供该资源并在您的配置中声明etag标头的显式值到版本字符串。

请注意,由于未提出要求,此处未详细说明此替代解决方案(未经测试)。

客户端实现不会被修改(这里它按照HTTP概念回报设计)。

答案 1 :(得分:1)

有多种方法可以实现这一点,但因为这是一个Flask应用程序,这里有一个使用HTTP。

如果版本没问题,只需返回相关的状态代码,例如200 OK。如果有必要,您可以在正文中添加JSON响应。如果你返回一个带烧瓶的字符串,状态代码将是200 OK,你可以在你的客户端检查它。

如果版本不同,请返回文件所在的URL。客户必须 下载文件。使用requests非常简单。以下是streaming requests下载文件的典型示例:

def get(url, chunk_size=1024):
    """ Download a file in chunks of n bytes """
    fn = url.split("/")[-1] # if you're url is complicated, use urlparse.
    stream = requests.get(url, stream=True)
    with open(fn, "wb") as local:
        for chunk in stream.iter_content(chunk_size=chunk_size):
            if chunk:
                f.write(chunk)
    return fn

非常简化。如果您的文件不是静态的并且无法在服务器上存活(可能不应该像软件更新补丁那样),那么您将不得不想办法从数据库中获取文件或者动态生成文件。