├── .gitignore ├── .idea ├── ht-candywebcache-demo-server.iml └── vcs.xml ├── packages_dema ├── .gitignore ├── .idea │ └── packages_dema.iml ├── __init__.py ├── db.py ├── http_server.py └── res.db ├── readme.md ├── test_packages ├── login_20160702.zip ├── login_20160703.zip └── login_20160704.zip └── upload_tools ├── bsdiff ├── packages ├── login.diff ├── login_20160702.zip ├── login_20160703.zip ├── login_20160704.zip └── readme.md └── upload.py /.gitignore: -------------------------------------------------------------------------------- 1 | CCDemo/CCDemo.xcworkspace/xcshareddata/ 2 | CCDemo/Pods/Pods.xcodeproj/xcuserdata/ 3 | ######################### 4 | # .gitignore file for Xcode4 and Xcode5 Source projects 5 | # 6 | # Apple bugs, waiting for Apple to fix/respond: 7 | # 8 | # 15564624 - what does the xccheckout file in Xcode5 do? Where's the documentation? 9 | # 10 | # Version 2.6 11 | # For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects 12 | # 13 | # 2015 updates: 14 | # - Fixed typo in "xccheckout" line - thanks to @lyck for pointing it out! 15 | # - Fixed the .idea optional ignore. Thanks to @hashier for pointing this out 16 | # - Finally added "xccheckout" to the ignore. Apple still refuses to answer support requests about this, but in practice it seems you should ignore it. 17 | # - minor tweaks from Jona and Coeur (slightly more precise xc* filtering/names) 18 | # 2014 updates: 19 | # - appended non-standard items DISABLED by default (uncomment if you use those tools) 20 | # - removed the edit that an SO.com moderator made without bothering to ask me 21 | # - researched CocoaPods .lock more carefully, thanks to Gokhan Celiker 22 | # 2013 updates: 23 | # - fixed the broken "save personal Schemes" 24 | # - added line-by-line explanations for EVERYTHING (some were missing) 25 | # 26 | # NB: if you are storing "built" products, this WILL NOT WORK, 27 | # and you should use a different .gitignore (or none at all) 28 | # This file is for SOURCE projects, where there are many extra 29 | # files that we want to exclude 30 | # 31 | ######################### 32 | 33 | ##### 34 | # OS X temporary files that should never be committed 35 | # 36 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 37 | 38 | .DS_Store 39 | 40 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 41 | 42 | .Trashes 43 | 44 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 45 | 46 | *.swp 47 | 48 | # 49 | # *.lock - this is used and abused by many editors for many different things. 50 | # For the main ones I use (e.g. Eclipse), it should be excluded 51 | # from source-control, but YMMV. 52 | # (lock files are usually local-only file-synchronization on the local FS that should NOT go in git) 53 | # c.f. the "OPTIONAL" section at bottom though, for tool-specific variations! 54 | # 55 | # In particular, if you're using CocoaPods, you'll want to comment-out this line: 56 | *.lock 57 | 58 | 59 | # 60 | # profile - REMOVED temporarily (on double-checking, I can't find it in OS X docs?) 61 | #profile 62 | 63 | 64 | #### 65 | # Xcode temporary files that should never be committed 66 | # 67 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this... 68 | 69 | *~.nib 70 | 71 | 72 | #### 73 | # Xcode build files - 74 | # 75 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" 76 | 77 | DerivedData/ 78 | 79 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" 80 | 81 | build/ 82 | 83 | 84 | ##### 85 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) 86 | # 87 | # This is complicated: 88 | # 89 | # SOMETIMES you need to put this file in version control. 90 | # Apple designed it poorly - if you use "custom executables", they are 91 | # saved in this file. 92 | # 99% of projects do NOT use those, so they do NOT want to version control this file. 93 | # ..but if you're in the 1%, comment out the line "*.pbxuser" 94 | 95 | # .pbxuser: http://lists.apple.com/archives/xcode-users/2004/Jan/msg00193.html 96 | 97 | *.pbxuser 98 | 99 | # .mode1v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html 100 | 101 | *.mode1v3 102 | 103 | # .mode2v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html 104 | 105 | *.mode2v3 106 | 107 | # .perspectivev3: http://stackoverflow.com/questions/5223297/xcode-projects-what-is-a-perspectivev3-file 108 | 109 | *.perspectivev3 110 | 111 | # NB: also, whitelist the default ones, some projects need to use these 112 | !default.pbxuser 113 | !default.mode1v3 114 | !default.mode2v3 115 | !default.perspectivev3 116 | 117 | 118 | #### 119 | # Xcode 4 - semi-personal settings 120 | # 121 | # Apple Shared data that Apple put in the wrong folder 122 | # c.f. http://stackoverflow.com/a/19260712/153422 123 | # FROM ANSWER: Apple says "don't ignore it" 124 | # FROM COMMENTS: Apple is wrong; Apple code is too buggy to trust; there are no known negative side-effects to ignoring Apple's unofficial advice and instead doing the thing that actively fixes bugs in Xcode 125 | # Up to you, but ... current advice: ignore it. 126 | *.xccheckout 127 | 128 | # 129 | # 130 | # OPTION 1: --------------------------------- 131 | # throw away ALL personal settings (including custom schemes! 132 | # - unless they are "shared") 133 | # As per build/ and DerivedData/, this ought to have a trailing slash 134 | # 135 | # NB: this is exclusive with OPTION 2 below 136 | xcuserdata/ 137 | 138 | # OPTION 2: --------------------------------- 139 | # get rid of ALL personal settings, but KEEP SOME OF THEM 140 | # - NB: you must manually uncomment the bits you want to keep 141 | # 142 | # NB: this *requires* git v1.8.2 or above; you may need to upgrade to latest OS X, 143 | # or manually install git over the top of the OS X version 144 | # NB: this is exclusive with OPTION 1 above 145 | # 146 | #xcuserdata/**/* 147 | 148 | # (requires option 2 above): Personal Schemes 149 | # 150 | #!xcuserdata/**/xcschemes/* 151 | 152 | #### 153 | # XCode 4 workspaces - more detailed 154 | # 155 | # Workspaces are important! They are a core feature of Xcode - don't exclude them :) 156 | # 157 | # Workspace layout is quite spammy. For reference: 158 | # 159 | # /(root)/ 160 | # /(project-name).xcodeproj/ 161 | # project.pbxproj 162 | # /project.xcworkspace/ 163 | # contents.xcworkspacedata 164 | # /xcuserdata/ 165 | # /(your name)/xcuserdatad/ 166 | # UserInterfaceState.xcuserstate 167 | # /xcshareddata/ 168 | # /xcschemes/ 169 | # (shared scheme name).xcscheme 170 | # /xcuserdata/ 171 | # /(your name)/xcuserdatad/ 172 | # (private scheme).xcscheme 173 | # xcschememanagement.plist 174 | # 175 | # 176 | 177 | #### 178 | # Xcode 4 - Deprecated classes 179 | # 180 | # Allegedly, if you manually "deprecate" your classes, they get moved here. 181 | # 182 | # We're using source-control, so this is a "feature" that we do not want! 183 | 184 | *.moved-aside 185 | 186 | #### 187 | # OPTIONAL: Some well-known tools that people use side-by-side with Xcode / iOS development 188 | # 189 | # NB: I'd rather not include these here, but gitignore's design is weak and doesn't allow 190 | # modular gitignore: you have to put EVERYTHING in one file. 191 | # 192 | # COCOAPODS: 193 | # 194 | # c.f. http://guides.cocoapods.org/using/using-cocoapods.html#what-is-a-podfilelock 195 | # c.f. http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 196 | # 197 | #!Podfile.lock 198 | # 199 | # RUBY: 200 | # 201 | # c.f. http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ 202 | # 203 | #!Gemfile.lock 204 | # 205 | # IDEA: 206 | # 207 | # c.f. https://www.jetbrains.com/objc/help/managing-projects-under-version-control.html?search=workspace.xml 208 | # 209 | #.idea/workspace.xml 210 | # 211 | # TEXTMATE: 212 | # 213 | # -- UNVERIFIED: c.f. http://stackoverflow.com/a/50283/153422 214 | # 215 | #tm_build_errors 216 | 217 | #### 218 | # UNKNOWN: recommended by others, but I can't discover what these files are 219 | # 220 | -------------------------------------------------------------------------------- /.idea/ht-candywebcache-demo-server.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages_dema/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /packages_dema/.idea/packages_dema.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /packages_dema/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NEYouFan/ht-candywebcache-demo-server/b3a51bf8d2477cc0cd116546bdd15c6d3e9b1f72/packages_dema/__init__.py -------------------------------------------------------------------------------- /packages_dema/db.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | import sqlite3 4 | 5 | 6 | class SqLiteCtrl(object): 7 | """ 8 | 数据库操作的基础接口封装 9 | """ 10 | def __init__(self): 11 | self.db = r"./res.db" # 在当前目录下创建隐藏的db文件 12 | 13 | def drop_table(self, drp_tb_sql): 14 | """ 15 | 16 | :return: 17 | """ 18 | con = sqlite3.connect(self.db) 19 | cur = con.cursor() 20 | 21 | cur.execute(drp_tb_sql) 22 | 23 | con.commit() 24 | 25 | cur.close() 26 | con.close() 27 | 28 | def create_table(self, crt_tb_sql): 29 | """ 30 | 对DB中的表versionInfo进行重设处理,仅在重设的时候被调用 31 | :return: 32 | """ 33 | con = sqlite3.connect(self.db) 34 | cur = con.cursor() 35 | 36 | cur.execute(crt_tb_sql) 37 | 38 | con.commit() 39 | 40 | cur.close() 41 | con.close() 42 | 43 | def do_insert(self, insert_sql, params_tuples): 44 | """ 45 | insert操作 46 | :param insert_sql: insert操作的sql语句 47 | :param params_tuples: [(), (), (), ......] 待插入的参数元组列表 48 | :return: 49 | """ 50 | con = sqlite3.connect(self.db) 51 | cur = con.cursor() 52 | 53 | for one in params_tuples: 54 | cur.execute(insert_sql, one) 55 | 56 | con.commit() 57 | 58 | cur.close() 59 | con.close() 60 | return 61 | 62 | def do_select(self, select_sql): 63 | """ 64 | 根据执行的sql语句执行select操作 65 | :param select_sql: 66 | :return: 67 | """ 68 | con = sqlite3.connect(self.db) 69 | cur = con.cursor() 70 | 71 | cur.execute(select_sql) 72 | 73 | date_set = cur.fetchall() 74 | 75 | cur.close() 76 | con.close() 77 | return date_set 78 | 79 | 80 | class VersionInfoTable(object): 81 | """ 82 | 业务表相关的处理 83 | """ 84 | table_name = "versionInfo" 85 | 86 | appID = "appID" 87 | appVersion = "appVersion" 88 | resID = "resID" 89 | resVersion = "resVersion" 90 | diffUrl = "diffUrl" 91 | diffMd5 = "diffMd5" 92 | fullUrl = "fullUrl" 93 | fullMd5 = "fullMd5" 94 | domain = "domain" 95 | 96 | fields = ("id", appID, appVersion, resID, resVersion, diffUrl, diffMd5, fullUrl, fullMd5, domain) 97 | 98 | drp_tb_sql = "drop table if exists versionInfo" 99 | crt_tb_sql = """ 100 | create table if not exists versionInfo( 101 | id integer primary key autoincrement unique not null, 102 | appID varchar(128), 103 | appVersion varchar(128), 104 | resID varchar(128), 105 | resVersion varchar(128), 106 | diffUrl varchar(128), 107 | diffMd5 varchar(128), 108 | fullUrl varchar(128), 109 | fullMd5 varchar(128), 110 | domain varchar(256) 111 | ); 112 | """ 113 | 114 | insert_sql = "insert into versionInfo " \ 115 | "(appID, appVersion, resID, resVersion, diffUrl, diffMd5, fullUrl, fullMd5, domain) " \ 116 | "values (?, ?, ?, ?, ?, ?, ?, ?, ?)" # ?为占位符 117 | 118 | select_all_sql = "select * from versionInfo" 119 | 120 | db_sqlite = SqLiteCtrl() 121 | 122 | ORDER_KEY = "ORDER_KEY" 123 | ORDER_TYPE = "ORDER_TYPE" 124 | LIMIT = "LIMIT" 125 | 126 | def __init__(self): 127 | self.order_key = "" 128 | self.order_type = "" 129 | self.limit = 0 130 | 131 | def drop_table(self): 132 | self.db_sqlite.drop_table(self.drp_tb_sql) 133 | return 134 | 135 | def create_table(self): 136 | """ 137 | 创建表信息 138 | :return: 139 | """ 140 | self.db_sqlite.create_table(self.crt_tb_sql) 141 | return 142 | 143 | def add_new_versions(self, new_versions): 144 | """ 145 | 向表中增加version数据信息 146 | :return: 147 | """ 148 | insert_params = [] 149 | for one in new_versions: 150 | insert_params.append((one[self.appID], one[self.appVersion], one[self.resID], one[self.resVersion], 151 | one[self.diffUrl], one[self.diffMd5], one[self.fullUrl], one[self.fullMd5], 152 | one[self.domain])) 153 | self.db_sqlite.do_insert(self.insert_sql, insert_params) 154 | return 155 | 156 | def get_latest_version(self, *args, **kw): 157 | """ 158 | 查询表中的version信息,查询最新版本的信息 159 | :param args: 160 | :param kw: 161 | :return: 162 | 说明:排序/limit部分其实可以采用对象式编程实现的,但是时间来不及了,先这样吧. 163 | """ 164 | 165 | # select部分 166 | select_part = "select " 167 | if len(args) == 0: 168 | select_part += "* " 169 | else: 170 | for one in args: 171 | select_part = select_part + one + ", " 172 | 173 | select_part = select_part[:-2] 174 | 175 | # where部分 176 | where_part = "" 177 | if len(kw): 178 | for (k, v) in kw.items(): 179 | if k == self.ORDER_KEY: 180 | self.order_key = v 181 | elif k == self.ORDER_TYPE: 182 | self.order_type = v 183 | elif k == self.LIMIT: 184 | self.limit = v 185 | elif 'tuple' in str(type(v)): 186 | where_part += k + " in " + str(v) + " and " 187 | else: 188 | where_part += k + "='" + v + "' and " 189 | 190 | where_part = "where " + where_part[:-4] 191 | 192 | # 连接起来 193 | select_sql = select_part + " from " + self.table_name + " " + where_part 194 | 195 | if len(self.order_key): 196 | select_sql += " order by " + self.order_key 197 | if len(self.order_type): 198 | select_sql += " " + self.order_type 199 | if self.limit: 200 | select_sql += " limit " + str(self.limit) 201 | 202 | print(select_sql) 203 | 204 | date_set = self.db_sqlite.do_select(select_sql) 205 | 206 | # 将结果取出 207 | result = [] 208 | for one in date_set: 209 | if len(args): 210 | result_item = {} 211 | for idx in range(len(args)): 212 | result_item[args[idx]] = one[idx] 213 | 214 | result.append(result_item) 215 | else: 216 | result_item = {} 217 | for idx in range(len(self.fields)): 218 | result_item[self.fields[idx]] = one[idx] 219 | 220 | result.append(result_item) 221 | 222 | return result 223 | 224 | def get_versions(self, *args, **kw): 225 | """ 226 | 查询表中的version信息 227 | :param args: 228 | :param kw: 229 | :return: 230 | select_all_sql = "select * from versionInfo" 231 | """ 232 | # select部分 233 | select_part = "select " 234 | if len(args) == 0: 235 | select_part += "* " 236 | else: 237 | for one in args: 238 | select_part = select_part + one + ", " 239 | 240 | select_part = select_part[:-2] 241 | 242 | # where部分 243 | where_part = "" 244 | if len(kw): 245 | for (k, v) in kw.items(): 246 | if 'tuple' in str(type(v)): 247 | where_part += k + " in " + str(v) + " and " 248 | else: 249 | where_part += k + "='" + v + "' and " 250 | 251 | where_part = "where " + where_part[:-4] 252 | 253 | # 连接起来 254 | select_sql = select_part + " from " + self.table_name + " " + where_part 255 | 256 | date_set = self.db_sqlite.do_select(select_sql) 257 | 258 | # 将结果取出 259 | result = [] 260 | for one in date_set: 261 | if len(args): 262 | result_item = {} 263 | for idx in range(len(args)): 264 | result_item[args[idx]] = one[idx] 265 | 266 | result.append(result_item) 267 | else: 268 | result_item = {} 269 | for idx in range(len(self.fields)): 270 | result_item[self.fields[idx]] = one[idx] 271 | 272 | result.append(result_item) 273 | 274 | return result 275 | 276 | 277 | if __name__ == "__main__": 278 | version_table = VersionInfoTable() 279 | version_table.create_table() 280 | # version_table.get_versions(appID="kaola") 281 | # version_table.get_versions(appID=("kaola", "kaola11")) 282 | # version_table.get_versions(VersionInfoTable.appVersion, appID=("kaola", "kaola11"), appVersion="1.0.0") 283 | # version_table.get_versions() 284 | # 285 | # new_versions = [] 286 | # version_table.add_new_versions(new_versions) 287 | 288 | result = VersionInfoTable().get_latest_version(appID="kaola", resID="login", 289 | ORDER_KEY="resVersion", ORDER_TYPE="DESC", LIMIT=1) 290 | 291 | result = version_table.get_versions() 292 | print(result) 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | -------------------------------------------------------------------------------- /packages_dema/http_server.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | # 1. 完成http模块; 4 | # 2. 完成DB模块; 5 | # - SQL操作; 6 | # - 检测sqllite存在性,并提示对应的信息(安装sqllite、启动sqllite server等) 7 | # - 检测表的存在,并提示对应信息(创建表等信息) 8 | # 3. 完成逻辑模块; 9 | # 4. 诊断模块 10 | # - 提供DB中version信息查询的处理功能 11 | 12 | # http server实现功能包括: 13 | # 1. 实现check_version的两个方式的post请求处理 14 | # 2. 资源包的上传处理 15 | # 3. 诊断模块的get处理 16 | 17 | import sys 18 | import http.server 19 | import socketserver 20 | import getopt 21 | import json 22 | 23 | from db import VersionInfoTable 24 | 25 | 26 | CODE_OK = 200 # 请求成功响应 27 | CODE_PROTOCOL_INVALID = 401 # 协议版本不支持 28 | CODE_APP_ID_INVALID = 402 # appID不支持 29 | CODE_SERVER_ERROR = 501 # 服务器错误 30 | CODE_OTHER_ERROR = 601 # 其他错误 31 | 32 | STATE_IS_LATEST = 0 # 提示本地资源已经是最新 33 | STATE_NEED_UPDATE = 1 # 提示需要更新 34 | STATE_NOT_THIS_RES = 2 # 请求资源不存在 35 | STATE_AUTO_RES = 3 # 自动补全的资源信息,请求中未携带资源信息 36 | 37 | 38 | class VersionCheck(object): 39 | 40 | resID = "resID" 41 | resVersion = "resVersion" 42 | 43 | """ 44 | 必填项,放在功能函数构造的时候 45 | """ 46 | def __init__(self, version, appID, appVersion, platform): 47 | self.version = version 48 | self.app_id = appID 49 | self.app_version = appVersion 50 | self.platform = platform 51 | 52 | self.res_infos = [] 53 | self.id_diff = False 54 | self.auto_fill = False 55 | 56 | self.res_latest_version = {} 57 | 58 | def set_option(self, resInfos, isDiff, autoFill): 59 | self.res_infos = resInfos 60 | self.id_diff = isDiff 61 | self.auto_fill = autoFill 62 | 63 | def all_res_version(self): 64 | """ 65 | 检查appid是否存在 66 | """ 67 | result = VersionInfoTable().get_versions("resID", "resVersion", appID=self.app_id) 68 | if len(result) == 0: 69 | return False 70 | else: 71 | for one in result: 72 | try: 73 | if self.res_latest_version[one["resID"]] < one["resVersion"]: 74 | self.res_latest_version[one["resID"]] = one["resVersion"] 75 | except: 76 | self.res_latest_version[one["resID"]] = one["resVersion"] # 第一次,那么就讲version赋值过去作为key的value 77 | 78 | return True 79 | 80 | def deal_noinput_res(self): 81 | """ 82 | 没有传入res的,补全全部的latest version的res资源 83 | :return: 84 | """ 85 | res_json = {"resInfos": []} 86 | 87 | if self.auto_fill: 88 | for k,v in self.res_latest_version.items(): 89 | result = VersionInfoTable().get_versions(appID=self.app_id, resID=k, resVersion=v) 90 | 91 | for one_result in result: 92 | one_item = {} 93 | one_item[VersionInfoTable.resID] = one_result[VersionInfoTable.resID] 94 | one_item[VersionInfoTable.resVersion] = one_result[VersionInfoTable.resVersion] 95 | one_item[VersionInfoTable.fullUrl] = one_result[VersionInfoTable.fullUrl] 96 | one_item[VersionInfoTable.fullMd5] = one_result[VersionInfoTable.fullMd5] 97 | 98 | one_item["state"] = 3 99 | domains = one_result[VersionInfoTable.domain].split(",") 100 | one_item["userData"] = json.dumps({"domains": domains}) 101 | 102 | res_json["resInfos"].append(one_item) 103 | else: 104 | pass 105 | 106 | return res_json 107 | 108 | def deal_withinput_res(self): 109 | """ 110 | 有传入res的处理方式 111 | :return: 112 | """ 113 | code = CODE_OK 114 | code_msg = "OK" 115 | res_json = {"resInfos": []} 116 | no_the_res = False 117 | 118 | # 先查找传入res的 119 | for input_one in self.res_infos: 120 | try: 121 | tmp_resID = input_one[self.resID] 122 | input_one[self.resVersion] 123 | 124 | if tmp_resID in self.res_latest_version: 125 | self.res_latest_version.pop(tmp_resID) 126 | else: 127 | no_the_res = True 128 | 129 | except Exception as e: 130 | continue 131 | 132 | one_item = {} 133 | 134 | result = VersionInfoTable().get_latest_version(appID=self.app_id, resID=input_one[self.resID], 135 | ORDER_KEY="resVersion", ORDER_TYPE="DESC", LIMIT=1) 136 | if len(result) == 0: 137 | one_item["state"] = 2 138 | one_item[VersionInfoTable.resID] = input_one[self.resID] 139 | else: 140 | for result_one in result: 141 | if result_one[VersionInfoTable.resVersion] == input_one[self.resVersion]: 142 | one_item["state"] = 0 143 | one_item[VersionInfoTable.resID] = input_one[self.resID] 144 | break 145 | 146 | elif result_one[VersionInfoTable.resVersion] > input_one[self.resVersion]: 147 | # 否则返回服务端最新的 148 | one_item[VersionInfoTable.resID] = result_one[VersionInfoTable.resID] 149 | one_item[VersionInfoTable.resVersion] = result_one[VersionInfoTable.resVersion] 150 | one_item[VersionInfoTable.fullUrl] = result_one[VersionInfoTable.fullUrl] 151 | one_item[VersionInfoTable.fullMd5] = result_one[VersionInfoTable.fullMd5] 152 | one_item[VersionInfoTable.diffUrl] = result_one[VersionInfoTable.diffUrl] 153 | one_item[VersionInfoTable.diffMd5] = result_one[VersionInfoTable.diffMd5] 154 | 155 | one_item["state"] = 1 156 | domains = result_one[VersionInfoTable.domain].split(",") 157 | one_item["userData"] = json.dumps({"domains": domains}) 158 | break 159 | 160 | else: 161 | # 说明传入的版本是更新的,暂时回复不用更新处理即可 162 | one_item["state"] = 0 163 | one_item[VersionInfoTable.resID] = input_one[self.resID] 164 | break 165 | 166 | res_json["resInfos"].append(one_item) 167 | 168 | # 然后执行需要补全的 169 | if self.auto_fill: 170 | for k,v in self.res_latest_version.items(): 171 | result = VersionInfoTable().get_versions(appID=self.app_id, resID=k, resVersion=v) 172 | 173 | for one_result in result: 174 | one_item = {} 175 | one_item[VersionInfoTable.resID] = one_result[VersionInfoTable.resID] 176 | one_item[VersionInfoTable.resVersion] = one_result[VersionInfoTable.resVersion] 177 | one_item[VersionInfoTable.fullUrl] = one_result[VersionInfoTable.fullUrl] 178 | one_item[VersionInfoTable.fullMd5] = one_result[VersionInfoTable.fullMd5] 179 | one_item[VersionInfoTable.diffUrl] = result_one[VersionInfoTable.diffUrl] 180 | one_item[VersionInfoTable.diffMd5] = result_one[VersionInfoTable.diffMd5] 181 | 182 | one_item["state"] = 3 183 | domains = one_result[VersionInfoTable.domain].split(",") 184 | one_item["userData"] = json.dumps({"domains": domains}) 185 | 186 | res_json["resInfos"].append(one_item) 187 | else: 188 | # if no_the_res: 189 | # code = CODE_OTHER_ERROR 190 | # code_msg = "The Input Res is No In Server" 191 | pass 192 | 193 | return res_json, code, code_msg 194 | 195 | 196 | def do_check(self): 197 | """ 198 | 实际的业务处理 199 | """ 200 | code = CODE_OK 201 | code_msg = "OK" 202 | 203 | if len(self.res_infos): 204 | res_json, code, code_msg = self.deal_withinput_res() 205 | else: 206 | res_json = self.deal_noinput_res() 207 | 208 | return res_json, code, code_msg 209 | 210 | 211 | """ 212 | Http业务处理的模块 213 | """ 214 | class PackageHttpHandler(http.server.BaseHTTPRequestHandler): 215 | """ 216 | 继承BaseHTTPRequestHandler用于http request的自定义处理相关 217 | """ 218 | 219 | """ 220 | get类reques的处理 221 | """ 222 | def do_GET(self): 223 | try: 224 | if self.path in "/api/version_infos": 225 | self.do_response(CODE_OK, "ok", self.do_get_all_versions_url()) 226 | 227 | except Exception as e: 228 | print("do_GET: "+ str(e)) 229 | self.do_response(CODE_OTHER_ERROR, str(e)) 230 | 231 | return 232 | 233 | """ 234 | post类request的处理 235 | """ 236 | def do_POST(self): 237 | try: 238 | if self.path in "/api/version_check/webapp": 239 | # 指定check version的url的操作 240 | self.do_checkversion_url() 241 | elif self.path in "/api/upload_version": 242 | # 执行版本信息的提交 243 | self.do_upload_version_url() 244 | elif self.path in "/api/get_latest_version": 245 | self.get_latest_version_url() 246 | 247 | except Exception as e: 248 | print("do_POST" + str(e)) 249 | self.do_response(CODE_OTHER_ERROR, str(e)) 250 | 251 | return 252 | 253 | """ 254 | 获取http Reques的内容 255 | """ 256 | def get_content_json(self): 257 | content_length = int(self.headers["Content-Length"]) 258 | content_str = self.rfile.read(content_length).decode('utf-8') 259 | content_json = json.loads(content_str) 260 | 261 | return content_json 262 | 263 | """ 264 | 返回值的处理 265 | """ 266 | def do_response(self, code, err_msg, data_json={}): 267 | 268 | res_json = {"code": code, "errMsg": err_msg, "data": data_json} 269 | res = json.dumps(res_json).encode('utf-8') 270 | 271 | self.protocol_version = 'HTTP/1.1' 272 | self.send_response(200) 273 | self.end_headers() 274 | self.wfile.write(res) 275 | 276 | return 277 | 278 | ############################################################################# 279 | def do_get_all_versions_url(self): 280 | """ 281 | 获取所有的版本信息 282 | :return: 283 | """ 284 | res_json = VersionInfoTable().get_versions() 285 | return res_json 286 | 287 | def do_upload_version_url(self): 288 | """ 289 | /api/upload_version 290 | """ 291 | content_json = self.get_content_json() 292 | new_versions = [] 293 | for one in content_json: 294 | try: 295 | one_version = {} 296 | one_version[VersionInfoTable.appID] = one[VersionInfoTable.appID] 297 | one_version[VersionInfoTable.appVersion] = one[VersionInfoTable.appVersion] 298 | one_version[VersionInfoTable.resID] = one[VersionInfoTable.resID] 299 | one_version[VersionInfoTable.resVersion] = one[VersionInfoTable.resVersion] 300 | one_version[VersionInfoTable.diffUrl] = one[VersionInfoTable.diffUrl] 301 | one_version[VersionInfoTable.diffMd5] = one[VersionInfoTable.diffMd5] 302 | one_version[VersionInfoTable.fullUrl] = one[VersionInfoTable.fullUrl] 303 | one_version[VersionInfoTable.fullMd5] = one[VersionInfoTable.fullMd5] 304 | one_version[VersionInfoTable.domain] = one[VersionInfoTable.domain] 305 | 306 | new_versions.append(one_version) 307 | except Exception as e: 308 | new_versions = [] 309 | print("do_upload_version: " + str(e)) 310 | 311 | VersionInfoTable().add_new_versions(new_versions) 312 | 313 | res_json = {"insert_cnt": len(new_versions)} 314 | 315 | self.do_response(CODE_OK, "ok", res_json) 316 | return 317 | 318 | def get_latest_version_url(self): 319 | """ 320 | /api/get_latest_version 321 | :return: 322 | """ 323 | content_json = self.get_content_json() 324 | latest_versions = [] 325 | 326 | try: 327 | appID = content_json["appID"] 328 | resID = content_json["resID"] 329 | 330 | result = VersionInfoTable().get_latest_version(appID=appID, resID=resID, 331 | ORDER_KEY="resVersion", ORDER_TYPE="DESC", LIMIT=1) 332 | for one in result: 333 | one_item = {} 334 | one_item[VersionInfoTable.resID] = one[VersionInfoTable.resID] 335 | one_item[VersionInfoTable.resVersion] = one[VersionInfoTable.resVersion] 336 | one_item[VersionInfoTable.fullUrl] = one[VersionInfoTable.fullUrl] 337 | one_item[VersionInfoTable.fullMd5] = one[VersionInfoTable.fullMd5] 338 | one_item[VersionInfoTable.diffUrl] = one[VersionInfoTable.diffUrl] 339 | one_item[VersionInfoTable.diffMd5] = one[VersionInfoTable.diffMd5] 340 | latest_versions.append(one_item) 341 | 342 | except Exception as e: 343 | print("get_latest_version_url: " + str(e)) 344 | self.do_response(CODE_OTHER_ERROR, "get latest version error.") 345 | return 346 | 347 | self.do_response(CODE_OK, "ok", latest_versions) 348 | return 349 | 350 | def do_checkversion_url(self): 351 | """ 352 | /api/version_check/webapp 353 | """ 354 | content_json = self.get_content_json() 355 | 356 | # 必选项 357 | version = content_json["version"] 358 | appID = content_json["appID"] 359 | appVersion = content_json["appVersion"] 360 | platform = content_json["platform"] 361 | 362 | resInfos = [] 363 | isDiff = False 364 | autoFill = False 365 | try: 366 | # 可选项 367 | resInfos = content_json["resInfos"] 368 | # resInfos = json.JSONDecoder().decode(tmp) 369 | except Exception as e: 370 | print("resInfos: " + str(e)) 371 | try: 372 | isDiff = bool(content_json['isDiff']) 373 | except Exception as e: 374 | print("isDiff: " + str(e)) 375 | try: 376 | autoFill = bool(content_json["autoFill"]) 377 | except Exception as e: 378 | print("autoFill: " + str(e)) 379 | 380 | # 准备执行业务处理 381 | check_version = VersionCheck(version, appID, appVersion, platform) 382 | check_version.set_option(resInfos, isDiff, autoFill) 383 | 384 | if check_version.all_res_version(): 385 | res_json, code, code_msg = check_version.do_check() 386 | self.do_response(code, code_msg, res_json) 387 | else: 388 | self.do_response(CODE_APP_ID_INVALID, "The appID is invalid.") 389 | return 390 | 391 | 392 | class Server(object): 393 | """ 394 | 服务类 395 | """ 396 | def __init__(self): 397 | self.host = "127.0.0.1" 398 | self.port = 8080 399 | self.is_clear = False 400 | 401 | def get_args(self): 402 | """ 403 | 服务执行的方式为:script -h host -p port 404 | :return: 405 | """ 406 | try: 407 | opt_list, args = getopt.getopt(sys.argv[1: ], "h: p: ") 408 | for opt, value in opt_list: 409 | if opt == '-h': 410 | self.host = value 411 | elif opt == '-p': 412 | self.port == int(value) 413 | elif opt == '-c': 414 | self.is_clear = True 415 | else: 416 | print("Usage:\tscript [-h host] [-p port] [-c]\n" 417 | " \t-c\t\tclear db table") 418 | except getopt.GetoptError as e: 419 | print(str(e)) 420 | 421 | def run(self): 422 | """ 423 | 启动服务并执行 424 | :return: 425 | """ 426 | self.get_args() 427 | 428 | if self.is_clear: 429 | VersionInfoTable().drop_table() 430 | 431 | VersionInfoTable().create_table() 432 | 433 | http_server = socketserver.TCPServer((self.host, self.port), PackageHttpHandler) 434 | 435 | http_server.serve_forever() 436 | 437 | return 438 | 439 | 440 | if __name__ == "__main__": 441 | server = Server() 442 | print('Start demo server on %s port %d' % (server.host, server.port)) 443 | server.run() 444 | -------------------------------------------------------------------------------- /packages_dema/res.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NEYouFan/ht-candywebcache-demo-server/b3a51bf8d2477cc0cd116546bdd15c6d3e9b1f72/packages_dema/res.db -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # WebCache-Server(Demo版) 2 | ## OverView 3 | 4 | WebCache Demo Server是WebCache项目自带的一个简单的本地测试服务器,包含资源“上传”、“下载”、检查更新等主要功能。WebCache Demo Server主要服务于在应用WebCache SDK时,验证SDK应用是否成功,以及检验各项功能是否正常。下图简单描述了适应于些Demo Server的WebCache框架结构图: 5 | 6 | ![](http://7xqcm1.com1.z0.glb.clouddn.com/Demo-Webcache-Server-structure.png) 7 | 8 | ## 环境说明 9 | 10 | * 安装python3.5 11 | 12 | Mac OS上自带python解析器,但需确保是python3.x,如果不是,需要更新至python3.x版本 13 | 14 | 这里不建议直接更新系统自带的python版本,可能会影响到你后续在使用python执行其他脚本时,出现python2.x与python3.x不兼容的问题。所以,简单点,直接安装python3.5后,不去更改原python的链接,而是新建一个链接指向可执行文件,如:python3,这样通过python3我们可以直接链向pytho3.5的目录,同时python命令仍然指向原系统版本。操作如下: 15 | 16 | 17 | ``` 18 | /Library/Frameworks/Python.framework/Versions/3.5 19 | 是它的默认安装路径,如果你的安装路径不是这个,需要相应改成你自己的安装路径 20 | 21 | sudo ln -s /Library/Frameworks/Python.framework/Versions/3.5/bin/pydoc3.5 /usr/bin/pydoc3 22 | sudo ln -s /Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /usr/bin/python3 23 | sudo ln -s /Library/Frameworks/Python.framework/Versions/3.5/bin/pythonw3.5 /usr/bin/pythonw3 24 | sudo ln -s /Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5m-config /usr/bin/python-config3 25 | ``` 26 | 27 | 28 | * 下载Demo-Webcache-Server包。解压之后目录结构如下: 29 | 30 | ![](http://7xqcm1.com1.z0.glb.clouddn.com/%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84.png) 31 | 32 | * packages_dema目录下是webcache-server相关的python脚本。可以进入此目录,直接通过下面命令启动server: 33 | 34 | ``` 35 | python3 http_server.py 36 | ``` 37 | 38 | 同时,需要更改测试的App(CCDemo)中的webcache server地址 39 | 40 | ``` 41 | config.serverAddress = @"127.0.0.1:8080"; 42 | ``` 43 | 44 | 首次启动server,会在此目录下创建一个本地的DB文件,此DB是用来存储上传res的版本信息的 45 | 46 | * upload_tools目录下面,包含upload.py,用来模拟本地“上传”操作 47 | 48 | ``` 49 | python3 upload.py 50 | ``` 51 | 52 | 如果之前没有安装过python的**requests**和**pycrypto**模块的话,需要先安装这两个模块。 53 | 54 | ``` 55 | sudo python3 -m pip install requests 56 | sudo python3 -m pip install pycrypto 57 | ``` 58 | 59 | 如果在使用pip安装时,有问题,可以参考[同时装了Python3和Python2,怎么用pip](https://www.zhihu.com/question/21653286) 60 | 61 | * test_packages目录下,包含几个测试用的资源包。 62 | 63 | * 文件下载server。通过python提供的SimpleHTTPServer module,直接在本地启动一个httpServer,将文件目录映射到本地的某一目录,这里为了统一和方便,请确保运行SimpleHTTPServer的目录与upload.py保持在同一路径 64 | 65 | ``` 66 | cd .../upload_tools 67 | python -m SimpleHTTPServer 68 | ``` 69 | 70 | ## upload-tool使用说明 71 | 72 | 这里,“上传”只是模拟的本地上传行为,即资源包会拷到SimpleHttpServer所映射的文件目录下,然后执行一次upload的http请求,将对应res包的版本信息更新到DB里面去。 73 | 74 | * 在upload-tool文件夹下,运行**python -m SimpleHTTPServer**(确保在SimpleHTTPServer的运行路径与upload-tool相同,否则不保证后续操作的正确性) 75 | * 在Demo-Webcache-Server/packages_dema下,运行**python http\_server.py** 76 | * 生成资源zip包 77 | * 修改upload.py脚本,指明需要“上传”的本地资源zip包的路径,resID,resVersion(请自行确保resID,resVersion, zipPath,domain的正确性)。 78 | 79 | ``` 80 | config_items = { 81 | "resID": "login", //资源ID,可根据自己的res进行修改 82 | "resVersion": "20160321", //资源版本,每次更新版本上传时要修改 83 | "appID": "kaola", //资源所在的appID,首次配置app资源时修改 84 | "domain": "www.baidu.com,www.163.com", //资源命中的domains,每次改变都需要修改 85 | "zipPath": "/Users/hzhehui/workspace/web-cache/test/login_20160321.zip", //待上传资源包的本地地址 86 | } 87 | ``` 88 | * 执行python3 upload.py,完成上传“请求” 89 | 90 | 上传成功的结果描述: 91 | 92 | * upload.py输出打印信息:{"data": {"insert_cnt": 1}, "errMsg": "ok", "code": 200} 93 | * upload_tools/packages目录下出现对应的资源包以及diff包 94 | * DB中的versionInfo表中会增加一条新的记录 95 | 96 | ## Demo-Server测试 97 | 98 | 运行集成web-Cache的App,通过上述upload-tool一节描述的资源上传方式,进行一次资源的上传(红色标注表明在执行上传upload脚本时,需要更改的项)。 99 | 100 | 如: 101 | 102 | ``` 103 | config_items = { 104 | "resID": "login", 105 | "resVersion": "20160702", 106 | "appID": "kaola", 107 | "domain": "www.baidu.com,www.163.com", 108 | "zipPath": "/Users/h zhehui/workspace/web-cache/test/login_20160702.zip" 109 | } 110 | ``` 111 | 112 | 113 | 1.配好上述资源包login_20160702.zip后,更改resID、resVersion、appID、domain、zipPath,通过upload.py模拟上传。 114 | 115 | 2.打开类似Charles等抓包工具抓包。启动App。这时可以看到api/version_check/webapp的请求,对应的response: 116 | 117 | ``` 118 | { 119 | "data": { 120 | "resInfos": [ 121 | { 122 | "userData": "{\"domains\": [\"www.baidu.com\", \"www.163.com\"]}", 123 | "fullMd5": "13c118e8c0284c0f968883a554bb9dd0", 124 | "resID": "login", 125 | "state": 3, 126 | "resVersion": "20160702", 127 | "fullUrl": "http://localhost:8000/packages/login_20160702.zip" 128 | }] 129 | }, 130 | "code": 200, 131 | "errMsg": "OK" 132 | } 133 | 134 | ``` 135 | 136 | 3.可以再进行一次资源更新的测试: 137 | 138 | ``` 139 | config_items = { 140 | "resID": "login", 141 | "resVersion": "20160703", 142 | "appID": "kaola", 143 | "domain": "www.baidu.com,www.163.com", 144 | "zipPath": "/Users/hzhehui/workspace/web-cache/test/login_20160703.zip", 145 | "root_url": "http://localhost:8000/packages" 146 | } 147 | ``` 148 | 149 | 配好新的资源包login_20160703.zip,更改resID、resVersion、appID、domain、zipPath,执行upload.py,模拟上传。 150 | 151 | 152 | 4.可再次启动App,这时可以看到api/version_check/webapp的请求,对应的response: 153 | 154 | ``` 155 | { 156 | "data": { 157 | "resInfos": [ 158 | { 159 | "userData": "{\"domains\": [\"www.baidu.com\", \"www.163.com\"]}", 160 | "fullMd5": "9c5a25ced1728d78518e096e9a00bdfb", 161 | "resID": "login", 162 | "diffMd5": "5250bcf52d7e259bc4cdd0a12cc059e9", 163 | "resVersion": "20160703", 164 | "fullUrl": "http://localhost:8000/packages/login_20160703.zip", 165 | "diffUrl": "http://localhost:8000/packages/login.diff", 166 | "state": 1 167 | } 168 | ] 169 | }, 170 | "code": 200, 171 | "errMsg": "OK" 172 | } 173 | ``` 174 | 175 | -------------------------------------------------------------------------------- /test_packages/login_20160702.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NEYouFan/ht-candywebcache-demo-server/b3a51bf8d2477cc0cd116546bdd15c6d3e9b1f72/test_packages/login_20160702.zip -------------------------------------------------------------------------------- /test_packages/login_20160703.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NEYouFan/ht-candywebcache-demo-server/b3a51bf8d2477cc0cd116546bdd15c6d3e9b1f72/test_packages/login_20160703.zip -------------------------------------------------------------------------------- /test_packages/login_20160704.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NEYouFan/ht-candywebcache-demo-server/b3a51bf8d2477cc0cd116546bdd15c6d3e9b1f72/test_packages/login_20160704.zip -------------------------------------------------------------------------------- /upload_tools/bsdiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NEYouFan/ht-candywebcache-demo-server/b3a51bf8d2477cc0cd116546bdd15c6d3e9b1f72/upload_tools/bsdiff -------------------------------------------------------------------------------- /upload_tools/packages/login.diff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NEYouFan/ht-candywebcache-demo-server/b3a51bf8d2477cc0cd116546bdd15c6d3e9b1f72/upload_tools/packages/login.diff -------------------------------------------------------------------------------- /upload_tools/packages/login_20160702.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NEYouFan/ht-candywebcache-demo-server/b3a51bf8d2477cc0cd116546bdd15c6d3e9b1f72/upload_tools/packages/login_20160702.zip -------------------------------------------------------------------------------- /upload_tools/packages/login_20160703.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NEYouFan/ht-candywebcache-demo-server/b3a51bf8d2477cc0cd116546bdd15c6d3e9b1f72/upload_tools/packages/login_20160703.zip -------------------------------------------------------------------------------- /upload_tools/packages/login_20160704.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NEYouFan/ht-candywebcache-demo-server/b3a51bf8d2477cc0cd116546bdd15c6d3e9b1f72/upload_tools/packages/login_20160704.zip -------------------------------------------------------------------------------- /upload_tools/packages/readme.md: -------------------------------------------------------------------------------- 1 | SimpleHTTPServer映射的文件目录 -------------------------------------------------------------------------------- /upload_tools/upload.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | import requests 4 | import sys 5 | import json 6 | import os 7 | import hashlib 8 | import base64 9 | import pyDes 10 | 11 | SERVER_IS_LATEST = 0 # 服务端已经是最新的 12 | SERVER_NEED_UPLOAD = 1 # 服务端有这个资源,但是版本较旧,可以更新 13 | SERVER_NOT_THIS_RES = 2 # 服务端没有这个资源 14 | SERVER_OTHER_ERROR = 3 # 服务端有错误,不执行上传 15 | 16 | 17 | # 用于设置相关信息的config项 18 | config_items = { 19 | "resID": "login", 20 | "resVersion": "20160702", 21 | "appID": "kaola", 22 | "domain": "www.baidu.com,www.163.com", 23 | "zipPath": "/Users/hzhehui/workspace/web-cache/NEYouFan/ht-candywebcache-demo-server/test_packages/login_20160702.zip" 24 | } 25 | #################### 配置项 26 | 27 | 28 | base_version_info = { 29 | "appVersion": "1.0.0", 30 | "appID": config_items["appID"], 31 | "resID": config_items["resID"], 32 | "resVersion": config_items["resVersion"], 33 | "domain": config_items["domain"], 34 | "zipPath": config_items["zipPath"], 35 | "fileServerPath": ".", 36 | "root_url": "http://localhost:8000/packages/" 37 | } 38 | 39 | post_data = { 40 | "appID": config_items["appID"], 41 | "resID": config_items["resID"] 42 | } 43 | 44 | 45 | def get_zippath(): 46 | """ 47 | 执行脚本参数处理 48 | :return: 49 | """ 50 | if len(sys.argv) == 2: 51 | base_version_info["zipPath"] = sys.argv[1] 52 | return True 53 | else: 54 | print("Usage:\t\tpython upload.py zip_file_path") 55 | return False 56 | 57 | 58 | def upload_package_file(old_file): 59 | """ 60 | 执行上传 61 | :param old_file: 62 | :return: 63 | """ 64 | url_path = "http://localhost:8080/api/upload_version" 65 | version_item = {} 66 | if create_version_info(version_item, old_file): 67 | post_json = [] 68 | # post_data = json.dumps(post_json.append(version_item)) 69 | post_json.append(version_item) 70 | 71 | ret = do_post(url_path, post_json) 72 | print(ret) 73 | else: 74 | print("upload_package_file: create_version_info Failed.") 75 | 76 | return 77 | 78 | def des_encrypt(data_src): 79 | des = pyDes.des("12344321", pyDes.ECB, padmode=pyDes.PAD_PKCS5) 80 | encrypt_data = des.encrypt(data_src) 81 | return encrypt_data 82 | 83 | def cal_md5(diff_file_name): 84 | md5 = hashlib.md5() 85 | diff_file = open(diff_file_name, 'rb') 86 | while True: 87 | data = diff_file.read(8192) 88 | if not data: 89 | break 90 | 91 | md5.update(data) 92 | md5_value = md5.hexdigest() 93 | return md5_value 94 | 95 | def create_md5(diff_file_name): 96 | """ 97 | 加密处理的 98 | :param diff_file_name: 99 | :return: 100 | """ 101 | md5_value = cal_md5(diff_file_name) 102 | 103 | desedData = des_encrypt(md5_value) 104 | output_value = base64.b64encode(desedData) 105 | 106 | retStr = str(output_value, encoding="utf-8") 107 | 108 | return retStr 109 | 110 | 111 | def create_version_info(version_item, old_file): 112 | """ 113 | 生成其他信息 114 | :return: 115 | """ 116 | try: 117 | # 文件拷贝 118 | copy_cmd = "cp " + base_version_info["zipPath"] + " " + base_version_info["fileServerPath"] + "/packages/" 119 | full_zip_md5 = create_md5(base_version_info["zipPath"]) 120 | if os.system(copy_cmd) != 0: 121 | return False 122 | 123 | # 生成相关信息 124 | tmp, package_name = os.path.split(base_version_info["zipPath"]) 125 | diff_name = package_name.split("_")[0] 126 | 127 | if old_file != "": 128 | # 生成diff文件的格式: ./bsdiff packages/login_20160703.zip packages/login_20160803.zip packages/login.diff 129 | p, file_name = os.path.split(old_file) 130 | cmd = "./bsdiff " + "packages/" + file_name + " packages/" + package_name + " packages/" + diff_name + ".diff" 131 | os.system(cmd) 132 | diff_file_name = "packages/" + diff_name + ".diff" 133 | #version_item["diffMd5"] = hashlib.md5(diff_file_name.encode()).hexdigest() 134 | version_item["diffMd5"] = create_md5(diff_file_name) 135 | version_item["diffUrl"] = base_version_info["root_url"] + diff_name + ".diff" 136 | else: 137 | version_item["diffMd5"] = "" # 首包,然后diff为空 138 | version_item["diffUrl"] = "" 139 | 140 | version_item["appVersion"] = base_version_info["appVersion"] # 该值待定 141 | version_item["appID"] = base_version_info["appID"] 142 | version_item["domain"] = base_version_info["domain"] 143 | version_item["resID"] = base_version_info["resID"] 144 | version_item["resVersion"] = base_version_info["resVersion"] 145 | 146 | version_item["fullUrl"] = base_version_info["root_url"] + package_name 147 | version_item["fullMd5"] = full_zip_md5 148 | 149 | except Exception as e: 150 | print("create_version_info: " + str(e)) 151 | return False 152 | 153 | return True 154 | 155 | 156 | def try_get_latest_version(): 157 | """ 158 | 尝试获取执行resID的最新的版本信息,用于和即将上传的做比较 159 | :param old_file: 160 | :return: 161 | """ 162 | 163 | url_path = "http://localhost:8080/api/get_latest_version" 164 | 165 | code, old_file = SERVER_OTHER_ERROR, "" 166 | 167 | try: 168 | result = do_post(url_path, post_data) 169 | result_json = json.loads(result) 170 | 171 | if result_json["code"] != 200: 172 | code = SERVER_OTHER_ERROR 173 | 174 | else: 175 | if len(result_json["data"]) == 0: 176 | code = SERVER_NOT_THIS_RES 177 | 178 | else: 179 | server_version = result_json["data"][0]["resVersion"] 180 | if server_version >= base_version_info["resVersion"]: 181 | print("server_version=====> " + result_json["data"][0]["resID"] + ": " + server_version) 182 | code = SERVER_IS_LATEST 183 | else: 184 | code = SERVER_NEED_UPLOAD 185 | old_file = result_json["data"][0]["fullUrl"] 186 | 187 | except Exception as e: 188 | print(e) 189 | code, old_file = SERVER_OTHER_ERROR, "" 190 | 191 | return code, old_file 192 | 193 | 194 | def do_post(url_path, data_json): 195 | """ 196 | 执行http的post请求 197 | :param url: 198 | :param post_data: 199 | :return: 200 | """ 201 | ret = requests.post(url_path, data=json.dumps(data_json)) 202 | return ret.text 203 | 204 | 205 | def do_get(url_path): 206 | """ 207 | 执行http的get请求 208 | :param url: 209 | :return: 210 | """ 211 | ret = requests.get(url_path) 212 | return ret.text 213 | 214 | 215 | def do_main(): 216 | old_file = "" 217 | state, old_file = try_get_latest_version() 218 | if state == SERVER_OTHER_ERROR: 219 | print("SERVER_OTHER_ERROR!!!!!.") 220 | return 221 | elif state == SERVER_IS_LATEST: 222 | print("SERVER_IS_LATEST!!!!!.") 223 | return 224 | 225 | upload_package_file(old_file) 226 | 227 | 228 | if __name__ == '__main__': 229 | # if get_zippath(): 230 | # pass 231 | do_main() 232 | 233 | 234 | 235 | 236 | 237 | # "resInfos": [ 238 | # { 239 | # "state": 0, 240 | # "resID": "login" 241 | # } 242 | # ] 243 | # 244 | # "resInfos": [] 245 | # 246 | # "resInfos": [ 247 | # { 248 | # "diffMd5": "45217d0f79ce7bd18b2e7e26466bfce8", 249 | # "fullUrl": "http://10.242.27.37:8000/test/login_20160703.zip", 250 | # "state": 1, 251 | # "diffUrl": "http://10.242.27.37:8000/test/login.diff", 252 | # "userData": { 253 | # "domains": [ 254 | # "m.kaola.com" 255 | # ] 256 | # }, 257 | # "resVersion": "20160703", 258 | # "resID": "login", 259 | # "fullMd5": "167ba73e342d67ee9549664dc82adaa9" 260 | # } 261 | # ] 262 | 263 | 264 | # [ 265 | # { 266 | # "diffMd5": "45217d0f79ce7bd18b2e7e26466bfce8", 267 | # "appID": "kaola", 268 | # "domain": "m.kaola.com", 269 | # "diffUrl": "http://10.242.27.37:8000/test/exit.diff", 270 | # "resVersion": "20160703", 271 | # "fullUrl": "http://10.242.27.37:8000/test/exit_20160703.zip", 272 | # "appVersion": "1.0.0", 273 | # "resID": "exit", 274 | # "fullMd5": "167ba73e342d67ee9549664dc82adaa9" 275 | # } 276 | # ] 277 | --------------------------------------------------------------------------------