前段时间,独自接手公司另一个项目组的App(该产品仅存的唯一的一个小伙伴离职了),本来心里很不情愿。因为代码实在是太古老了,而且经过了n手的像我这样的断代接收,长期处于半维护状态,所以我都是一边看代码一边吐槽。并不是说前面的小伙伴的技术不好,而是经过多年多次流转,每一代都风格迥异就形成了现在这副冗余不堪的样子。
我们自己的App是用当前最流行的Jenkins+Git+Gradle持续集成,但这个App就惨了,你不得不自己打包发到群里,可想而知多少麻烦,就想着接入接入我们的Jenkins服务器,自动打包。但是被权限问题折腾过后,而且这段时间大家都忙着一个超高优先级的项目,就先搁置了。
前天就想着为什么不自己写一套呢。反正有免费的服务器用,那就物尽其用。
首先要实现的功能:
1,自动打包-Python实现
2,文件上传下载服务器-Python实现
3,包列表前端展示-HTML
4,包列表展示和下载移动端实现
经过两天的加班加点,终于实现啦,先看效果:
移动端apk列表:
前端apk列表:
后端文件服务核心代码:
import os
from flask import request, jsonify
from flask import send_from_directory, abort
from flask_restful import Resource, reqparse
from werkzeug.utils import secure_filename
from modules.APKRecord import APKRecordModel
from support.CHelper import get_millisecond, get_file_name
basedir = os.path.abspath(os.path.dirname(__file__))
# set(['jpeg', 'apk'])
ALLOWED_EXTENSIONS = {'apk'}
# 用于判断文件后缀
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
class APKRecord(Resource):
@staticmethod
def get():
return {"You should have a file_name": 404}, 403
# @staticmethod
# def get(file_name):
# if os.path.isfile(os.path.join('uploadApk', file_name)):
# return send_from_directory('uploadApk', file_name, as_attachment=True)
# abort(404)
@staticmethod
def get(file_name):
if os.path.isfile(os.path.join(r'D:\_pythonProj\CFlaskProj\resources\uploadApk', file_name)):
return send_from_directory(r'D:\_pythonProj\CFlaskProj\resources\uploadApk', file_name, as_attachment=True)
else:
abort(404)
class APKRecordList(Resource):
parser = reqparse.RequestParser()
@staticmethod
def get():
file_dir = os.path.join(basedir, "uploadApk")
if not os.path.exists(file_dir):
os.makedirs(file_dir)
apkList = []
if os.path.exists(file_dir):
for root, dirs, files in os.walk(file_dir):
for file in files:
item = APKRecordModel(file)
apkList.append(item.json())
return jsonify(result='success', data=apkList)
def post(self):
file_dir = os.path.join(basedir, "uploadApk")
if not os.path.exists(file_dir):
os.makedirs(file_dir)
f = request.files['apk_name'] # 从表单的file字段获取文件,apk_name为该表单的name值
if f and allowed_file(f.filename): # 判断是否是允许上传的文件类型
fname = secure_filename(f.filename)
ext = fname.rsplit('.', 1)[1] # 获取文件后缀
name = get_file_name(fname)
timestamp = get_millisecond()
new_filename = name + '_' + str(timestamp) + '.' + ext # 修改了上传的文件名加上了 _时间戳
f.save(os.path.join(file_dir, new_filename)) # 保存文件到uploadApk目录
return {"code": 0, "msg": "upload success", "fileName": new_filename, "fileTime": timestamp}, 201
else:
return {"code": 1001, "msg": "file not allowed"}
这里要注意的一点的是:不同的环境(比如Mac环境和Windows环境),这个地址也要改变:
Mac环境下:
@staticmethod
def get(file_name):
if os.path.isfile(os.path.join('uploadApk', file_name)):
return send_from_directory('uploadApk', file_name, as_attachment=True)
abort(404)
Windows环境下:
@staticmethod
def get(file_name):
if os.path.isfile(os.path.join(r'D:\_pythonProj\CFlaskProj\resources\uploadApk', file_name)):
return send_from_directory(r'D:\_pythonProj\CFlaskProj\resources\uploadApk', file_name, as_attachment=True)
else:
abort(404)
前端展示:还是用的最简单jquery
<script>
$.ajax({
url: "/apk/records",
dataType: 'json',
success: function (res) {
res.data.map(item => {
let liNode = document.createElement("li");
liNode.className = "my_record_list_li"
let labelNode = document.createElement("label")
labelNode.className = "my_record_list_label"
let spanNode = document.createElement("span")
spanNode.innerHTML = item.fileTime
labelNode.append(spanNode)
let delNode = document.createElement("a");
delNode.className = "delete_record"
delNode.href = item.downloadUrl
delNode.innerHTML = "下载"
labelNode.append(delNode)
let aNode = document.createElement("a");
aNode.className = "my_record_list_a"
aNode.href = item.downloadUrl
aNode.innerHTML = item.fileName
liNode.append(aNode)
liNode.append(labelNode)
$("#record_list_container").append(liNode)
})
document.getElementById('record_num_value').innerHTML = "共" + res.data.length + "个包"
}
})
</script>
同样实现了,通过网页上传代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form id="form1" method="post" action="http://xx.xxxx.xxxx.xxxx:5000/apk/records" enctype="multipart/form-data">
<div>
<input id="file1" type="file" name="apk_name"/>
<input type="submit">提交</input>
</div>
</form>
</body>
</html>
当然打包,上传脚本核心代码:
# 请求服务器
def request_url(url, params):
cookies = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies), MultipartPostHandler.MultipartPostHandler)
try:
print('request url:' + url)
print('request params:' + str(params))
response = opener.open(url, params)
return response.read()
except Exception as e:
print(str(e))
# 上传文件到服务器
def upload_file(url, apkpath):
params = {'username': reinforce_username, 'password': reinforce_password, 'apk': open(apkpath, 'rb'),
'tactics_id': reinforce_tactics_id}
response = request_url(url, params)
data = json.loads(response)
return data
搞定这些东西还是有点成就感的,感觉应该可以写成gradle脚本,甚至生成gradle插件,这样服务器就没必要配置Python环境,稍后有时间写成插件。