自己动手实现自动打包,上传下载服务,前端展示

前段时间,独自接手公司另一个项目组的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环境,稍后有时间写成插件。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352