JB的測試之旅-jenkins打包產物生成二維碼

前言

無風不起浪,爲何會作這個事情,就要由前幾天講起了。。 php

image.png-26.1kB

悲劇了

小公司沒有資源,由於不少內測都是用第三方的,這邊用的是蒲公英;css

在某日早上,開發提測,打包,上傳pgy,準備給業務方體驗的時候,結果點擊查看下載頁按鈕,彈出這貨; html

image.png-98.1kB

一開始覺得是本身手誤,而後再上傳幾回,依然顯示這個界面,也沒有任何報錯信息,懵逼啊,以前都用的好好的,什麼鬼? jquery

image.png-67.1kB

折騰半天,無望,拿起手機,看到有短信,點開一開,顯示這個: android

image.png-32.9kB

這裏面說到再也不接受金融類應用在該平臺分發,我司產品雖然是資訊類產品,但內容的確是金融相關的,好像沒毛病;ios

操做起來有點麻煩

公司某項目的打包產品是一個zip,是當打包完成後把apk跟ipa壓縮成一個zip輸出,而使用者須要下載這個zip,解壓,電腦連手機/模擬器安裝,方可體驗; nginx

image.png-77kB

整個鏈路過長,也比較麻煩,所以就想着兩點:git

  • 打包拆分,支持安卓、ios分開打包,否則有時候驗證一個平臺的問題須要打兩個包,打包時長成本問題;
  • 打包產物顯示二維碼便捷下載

在正式開始以前,先說明下,testerhome其實有相似的文章,以下圖: github

在此輸入正文

對應的文章都寫的挺好的,可是輪子嘛,仍是要親力親爲印象才深入,並且會針對對應文章缺少的內容進行相應補充,儘量更加詳細把過程寫出來;web

jenkins顯示圖片

這裏不會再講述jenkins是什麼,怎麼安裝之類的內容,若是有疑問,請點擊此處第二處查看;

想要作成的效果是這樣的:

  • 支持修改文件描述
  • 支持顯示二維碼
    image.png-94.9kB

插件安裝

jenkins不支持上面兩個操做的,所以須要安裝插件來使用;

  • Build Name Setter ,用於修改Build名稱
  • description setter,用於在修改Build描述信息,在描述信息中增長顯示QRCode(二維碼)

直接在Jenkins的插件管理頁面搜索上述插件,點擊安裝便可

image.png-16.6kB
image.png-37.5kB

怎麼用

點擊對應job的設置界面;

Build Name Setter

點擊Build Environment,找到set build name,默認是#${BUILD_NUMBER},這裏能夠自定義,以下修改爲#${BUILD_NUMBER}jbtest,執行任務後的結果是這樣的;

image.png-63.8kB

description setter

這個是在Post-build Actions裏面,將<img src='qr_code_url'>寫入到build描述信息中便可;

但填寫完發現跟預想的不一致,這是由於Jenkins出於安全的考慮,全部描述信息的Markup Formatter默認都是採用Plain text模式,在這種模式下是不會對build描述信息中的HTML編碼進行解析的。

要改變也很容易,Manage Jenkins -> Configure Global Security,將Markup Formatter的設置更改成Safe HTML便可。

image.png-25.4kB
更改配置後,咱們就能夠在build描述信息中採用HTML的img標籤插入圖片了。

<img src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3559712731,2278077975&amp;fm=26&amp;gp=0.jpg" style="background-color: rgb(177, 197, 163); width: 286px; height: 189px;">
複製代碼

保存後,執行任務,會就會顯示url對應的圖片了;

image.png-174.2kB

到這裏,jenkins上顯示圖片的問題,就這樣解決啦~

image.png-4.8kB

小小結

jenkins顯示圖片及修改任務描述,須要安裝兩個插件,而且須要傳一個圖片的img標籤過來便可;

jenkins任務結果收集產物

這裏額外說起一個點,若是job裏面是有產物的,好比apk等文件,默認構建後是不會顯示出來的,以下圖:

image.png-330.2kB

那怎樣讓其在右側顯示出來?仍是打開job的設置項,Post-build Actions,選擇歸檔成品/Archives build artifacts,在Files to archive裏面輸入內容就好啦;

定位文件時,能夠經過正則表達式進行匹配,也能夠調用項目的環境變量;多個文件經過逗號進行分隔;

${OUTPUT_FOLDER}/*.ipa,*.txt,QRCode.png

#注意,${OUTPUT_FOLDER}是
複製代碼

添加後的配置頁面以下圖所示:

image.png-19.2kB

從新構建任務,就能夠看到對應的產物啦;

image.png-49.1kB

分發平臺

首先說明,非廣告貼,非廣告貼,這節除了介紹分發平臺,也會介紹不使用分發平臺時怎麼搞,任君選擇;

上網搜了下,目前國內比較有名且還能用的分發平臺,就是蒲公英fir.im

蒲公英

點擊上面的地址打開官網,註冊登陸,點擊文檔,會有API說明;

簡單看了下,支持的功能蠻多的,好像能夠,而本章的重點是上傳APP,能夠搜索框輸入上傳APP,也能夠點擊連接直接跳轉;

image.png-23.8kB

仔細看了下response,有二維碼地址,good,就是你啦;

常規參數說明

參數 別稱 說明
_api_key API Key API Key,用來識別API調用者的身份,如不特別說明,每一個接口中都須要含有此參數。對於同一個蒲公英的註冊用戶來講,這個值在固定的;
userKey User Key 用戶Key,用來標識當前用戶的身份,對於同一個蒲公英的註冊用戶來講,這個值在固定的;
appKey App Key 表示一個App組的惟一Key。例如,名稱爲'微信'的App上傳了三個版本,那麼這三個版本爲一個App組,該參數表示這個組的Key。這個值顯示在應用詳情--應用概述--App Key。
buildKey Build Key Build Key是惟一標識應用的索引ID,能夠經過獲取App全部版本取得

_api_keyuserKey在登陸狀態下,點擊網頁的按鈕便可獲取;

上傳App

參數太多了,懶的貼了,直接上代碼吧;

Linux

這是官網給的例子,Linux下直接使用curl命令上傳便可;

curl -F 'file=@/c/Users/jb/Desktop/jb-android-3.4.1.30402-release-1812251912.apk' -F '_api_key=你的key' https://www.pgyer.com/apiv2/app/upload
複製代碼

執行後等待上傳完便可:

image.png-52.7kB
從返回的結果來看,是有一個 buildQRCodeURL,就是拿這個給到jenkins那邊的;

Python 環境,py3

import requests
import sys
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

upload_url = "https://www.pgyer.com/apiv2/app/upload"
_api_key = "你的key"
apk_path = "你要上傳的文件路徑"
def pgy_uploadFile():
    # 獲取運行傳遞過來的參數
    # _apk_path = sys.argv[1]
    # 上傳apk
    try:
        file = {'file': open(apk_path, 'rb')}
        param = {'_api_key': _api_key}
        req=requests.post(url=upload_url,files=file,data=param,verify=False)
        if (req.status_code == 200):
            print(req.json().get("data")["buildQRCodeURL"])
        else:
            print("上傳失敗,狀態碼: "+req.status_code)
    except Exception as e:
        print("upload:" + e)
if __name__ == '__main__':
    pgy_uploadFile()
複製代碼

若是是須要傳參給腳本,就直接用sys.argv來獲取,腳本原本沒作太多兼容處理,將就用吧;

最後會輸出二維碼地址,拿這個地址傳給jenkins就好啦;

image.png-12.8kB

結合jenkins玩玩

上面說起到,jenkins顯示二維碼是利用img src來處理,可是這個蒲公英返回的二維碼地址是每次都不一樣的呢,那怎麼搞?按照常理來講,是把src的值寫成變量就好啦;

image.png-40.6kB

其實就是寫成一個變量就行了,可是也由於url自己每次都變化,所以不能直接貼url,而是把url下載下來,固定下來一個名稱,變量直接取這個路徑便可;

那上面的img標籤就會變成這樣啦:

<img src="${BUILD_URL}/artifact/QRCode.png" style="background-color: width: 286px; height: 189px;">
    # 上面有多餘的樣式,由於圖片是隨便複製的,比較大,所以作了下限制,非必選;
複製代碼

這裏可能有同窗會問題,這個${BUILD_URL}是怎麼來的,表明什麼意思;

${BUILD_URL}是jenkins的內置變量,表明着顯示當前構建的URL地址,文章尾部會列出經常使用的jenkins變量;

好比上,假如二維碼的連接是這樣:

jenkineUrl/job/jobName/34/artifact/QRCode.png
複製代碼

那麼,jenkineUrl/job/jobName/34這一串就是${BUILD_URL}

既然須要圖片,那咱們就下載圖片吧,反正都有url了;

def pgy_doanloadQRCode(QRCodeURL):
    print("準備下載二維碼")
    filename = os.getcwd()+"/QRCode.png"
    with open(filename, 'wb') as f:
        # 以二進制寫入的模式在本地構建新文件
        header = {
            'User-Agent': '"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",'
            , 'Referer': QRCodeURL}
        f.write(requests.get(QRCodeURL, headers=header).content)
        print("%s下載完成" % filename)
複製代碼

結果截圖

因pgy須要上傳apk或ipa,由於爲了方便,直接hardcore了一個圖片url來演示結果;

image.png-51.2kB
image.png-138.7kB

fir.im

點擊這裏跳轉到官網,看了下,實名認證用戶有 100 次/日的免費下載限額,未實名,僅有10次/日的免費下載限額;

通常來講,小公司,天天100次夠用啦,除非產品夠多,或者打包頻繁;

實名好麻煩,還要手持證件照,不要緊,反正有10次,夠用啦;

而後去看api文章,咦,response竟然沒有二維碼字段?那手動上傳一個應用試試看,結果。。

image.png-6.3kB
當時內心的疑問就以下圖同樣,好吧,再見;
image.png-89.4kB

小小結

網上找了下分發平臺,國內比較有名且還能用的只剩下蒲公英跟fir.im,然而fir.im須要實名才能玩,那就剩下蒲公英了,親自接入下蒲公英,接入比較簡單,並且支持的字段也很多,目前來看,比較推薦,省去很多事;

造輪子

此時有同窗可能會有疑問,我司產品比較機密,不想用第三方,本身能夠造一個輪子嗎?

image.png-36.6kB

這個問題很是好,沒錯,能夠的,簡單想一想了,這個輪子須要啥?

  • 一個界面,提供上傳文件按鈕;
  • 文件支持點擊下載,支持鼠標移動到文件時顯示對應的二維碼;
  • 一臺服務器;

想的界面很簡單,原本一開始是想着安裝個phpstudy就好啦,可是忽然想起,前幾天看到大佬發了個截圖:

image.png-119.4kB

想着差很少,也順便弄一個倉庫唄,比較方便;

但jb不懂這個怎麼弄,就跑去問老大了;

image.png-94.4kB

聽老大說挺簡單的,可是啊,jb只聽過ng,沒真正玩過啊,哪裏簡單的了,哭;

image.png-9.4kB

因而乎就去簡單瞭解ng下了,漫漫人生路;

nginx簡單介紹

也許沒聽過nginx,可是不要緊,Apache確定是聽過的,這二者都屬於http server,所以,nginx一樣是一款開源的HTTP服務器軟件;

主要拿來幹嗎

  • 反向代理
  • 負載均衡
  • HTTP服務器(包含動靜分離)
  • 正向代理

反向代理

反向代理應該是Nginx作的最多的一件事了;

什麼是反向代理呢,如下是百度百科的說法:

反向代理(Reverse Proxy)方式是指以代理服務器來接受internet上的鏈接請求,而後將請求轉發給內部網絡上的服務器,並將從服務器上獲得的結果返回給internet上請求鏈接的客戶端,此時代理服務器對外就表現爲一個反向代理服務器。簡單來講就是真實的服務器不能直接被外部網絡訪問,因此須要一臺代理服務器,而代理服務器能被外部網絡訪問的同時又跟真實服務器在同一個網絡環境,固然也多是同一臺服務器,端口不一樣而已。
複製代碼

下面貼上一段簡單的實現反向代理的代碼

server {
        listen       80;                                                         
        server_name  localhost;                                               
        client_max_body_size 1024M;

        location / {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host:$server_port;
        }
    }
複製代碼

保存配置文件後啓動Nginx,這樣當咱們訪問localhost的時候,就至關於訪問localhost:8080了;

負載均衡

負載均衡其意思就是分攤到多個操做單元上進行執行,例如Web服務器、FTP服務器、企業關鍵應用服務器和其它關鍵任務服務器等,從而共同完成工做任務。

簡單而言就是當有2臺或以上服務器時,根據規則隨機的將請求分發到指定的服務器上處理,負載均衡配置通常都須要同時配置反向代理,經過反向代理跳轉到負載均衡。

HTTP服務器

Nginx自己也是一個靜態資源的服務器,當只有靜態資源的時候,就可使用Nginx來作服務器,同時如今也很流行動靜分離,就能夠經過Nginx來實現,首先看看Nginx作靜態資源服務器;

server {
        listen       80;                                                         
        server_name  localhost;                                               
        client_max_body_size 1024M;


        location / {
               root   e:\wwwroot;
               index  index.html;
           }
    }
複製代碼

這樣若是訪問http://localhost 就會默認訪問到E盤wwwroot目錄下面的index.html,若是一個網站只是靜態頁面的話,那麼就能夠經過這種方式來實現部署。

動靜分離

動靜分離是讓動態網站裏的動態網頁根據必定規則把不變的資源和常常變的資源區分開來,動靜資源作好了拆分之後,咱們就能夠根據靜態資源的特色將其作緩存操做,這就是網站靜態化處理的核心思路;

upstream test{  
       server localhost:8080;  
       server localhost:8081;  
    }   
      
    server {  
        listen       80;  
        server_name  localhost;  
  
        location / {  
            root   e:\wwwroot;  
            index  index.html;  
        }  
          
        # 全部靜態請求都由nginx處理,存放目錄爲html 
        location ~ \.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {  
            root    e:\wwwroot;  
        }  
          
        # 全部動態請求都轉發給tomcat處理 
        location ~ \.(jsp|do)$ {  
            proxy_pass  http://test;  
        }  
          
        error_page   500 502 503 504  /50x.html;  
        location = /50x.html {  
            root   e:\wwwroot;  
        }  
    }
複製代碼

這樣就能夠吧HTML以及圖片和css以及js放到wwwroot目錄下,而tomcat只負責處理jsp和請求;

例如當咱們後綴爲gif的時候,Nginx默認會從wwwroot獲取到當前請求的動態圖文件返回,固然這裏的靜態文件跟Nginx是同一臺服務器,咱們也能夠在另一臺服務器,而後經過反向代理和負載均衡配置過去就行了,只要搞清楚了最基本的流程,不少配置就很簡單了,另外localtion後面實際上是一個正則表達式,因此很是靈活;

正向代理

意思是一個位於客戶端和原始服務器(origin server)之間的服務器,爲了從原始服務器取得內容,客戶端向代理髮送一個請求並指定目標(原始服務器),而後代理向原始服務器轉交請求並將得到的內容返回給客戶端。客戶端才能使用正向代理。

nginx經常使用命令

啓動nginx

service nginx start
複製代碼

中止nginx

nginx -s stop
複製代碼

查看nginx進程

ps -ef | grep nginx 
複製代碼

平滑啓動nginx

nginx -s reload
複製代碼

平滑啓動的意思是在不中止nginx的狀況下,重啓nginx,從新加載配置文件,啓動新的工做線程,完美中止舊的工做線程。

強制中止nginx

pkill -9 nginx
複製代碼

檢查對nginx.conf文件的修改是否正確

nginx -t -c /etc/nginx/nginx.conf
or 
nginx -t
複製代碼

查看nginx的版本

nginx -v
or
nginx -V
複製代碼

端口開放

因阿里雲默認是安裝了nginx 1.6版本,所以這塊不說明;

直接在阿里雲找到安全組規則,添加對應的對口,就能夠用公網IP訪問啦;

image.png-36.1kB

修改默認端口

因nginx默認是使用80端口的,若是須要修改,須要去配置文件修改;

nginx安裝文件在/etc/nginx,打開後發現裏面有個nginx.conf,查看發現裏面沒有端口信息,可是最後一行插入了*.conf文件,那咱們就跟着這目錄找;

image.png-81kB

cd到conf.d目錄,發現裏面只有一個default.conf文件,編輯查看,發現裏面有個listen端口,這個就是了,修改爲像要的端口,保存便可;

image.png-51.2kB

而後輸入nginx -s reload重啓服務器,而後再用公網IP+端口訪問下,也會顯示Welcome to nginx!

增長端口

有同窗可能問,那想加多幾個端口能夠嗎?

沒問題的,仍是來到default.conf文件,在原來的server下新增一個就好啦,以下:

server {
    listen       8083;
    server_name  location;

    #charset koi8-r;
    #access_log /var/log/nginx/host.access.log main;

    location / {
	root /home/file_dir;
        autoindex on;   #開啓nginx目錄瀏覽功能
        autoindex_exact_size off;   #文件大小從KB開始顯示
        autoindex_localtime on;   #顯示文件修改時間爲服務器本地時間

        add_after_body /autoindex.html;
        charset utf-8;
    }

複製代碼

autoindex

nginx有一個目錄瀏覽功能(autoindex),可是呢,默認是不容許列出整個目錄的,若是有需求,就用上面的新增端口的方式來操做就好啦;

而上面這個autoindex.html文件點擊下發連接下載便可;

連接:https://pan.baidu.com/s/1oiukkMAILzq9lHwCKzy-0w 
提取碼:7ytc 
複製代碼

最後,整個效果以下:

image.png-70kB

還能夠解析README.md,騷啊;

image.png-113.9kB

root&alias

在弄ng的配置文件時,看到過別人是這樣弄的;

location /ware {
    alias /lvdata/warehouse/;
複製代碼

而本身基本上只會這麼弄的:

location / {  
    root   e:\wwwroot;  
複製代碼

當時內心就想,這二者有什麼區別?

簡介

nginx指定文件路徑有兩種方式rootalias;

root與alias主要區別在於nginx如何解釋location後面的uri,這會使二者分別以不一樣的方式將請求映射到服務器文件上。

語法

root的用法

句法:	root path;
默認:	root html;
語境:	http,server,location,if in location
複製代碼

示例1:

location ^~ /request_path/dirt/ {
    root /local_path/dirt/;
  }
複製代碼

當客戶端請求 /request_path/image/file.ext的時候,Nginx把請求解析映射爲/local_path/dirt/request_path/dirt/file.ext

實例2:

location ^~ /t/ {
 root /www/root/html/;
}
複製代碼

若是一個請求的URI是/t/a.html時,web服務器將會返回服務器上的/www/root/html/t/a.html的文件; alias的用法

句法:	alias path;
默認:	-
語境:	location
複製代碼

示例1:

location /request_path/dirt/ {
    alias /local_path/dirt/file/;
}
複製代碼

當客戶端請求 /request_path/dirt/file.ext 的時候,Nginx把請求映射爲/local_path/dirt/file/file.ext 注意這裏是file目錄,由於alias會把location後面配置的路徑丟棄掉(好比/request_path/dirt/one.html,到alias那裏就剩one.html了),把當前匹配到的目錄指向到指定的目錄。

示例2:

location ^~ /t/ {
 alias /www/root/html/new_t/;
}
複製代碼

若是一個請求的URI是/t/a.html時,web服務器將會返回服務器上的/www/root/html/new_t/a.html的文件;

綜合例子

location /abc/ {
    alias /home/html/abc/;
}
複製代碼

在這段配置下,http://test/abc/a.html 就指定的是 /home/html/abc/a.html;

這段配置亦可改爲使用 root 標籤:

location /abc/ {
    root /home/html/;
}
複製代碼

這樣,nginx 就會去找 /home/html/ 目錄下的 abc 目錄了,獲得的結果是相同的。

可是,若是把 alias 的配置改爲

location /abc/ {
    alias /home/html/def/;
}
複製代碼

那麼 nginx 將會直接從 /home/html/def/ 取數據,例如訪問 http://test/abc/a.html 指向的是 /home/html/def/a.html;

這段配置還不能直接使用 root 配置,若是非要配置,只有在 /home/html/ 下創建一個 def->abc軟 link(快捷方式)了。

通常狀況下,在 location / 中配置 root,在 location /other 中配置 alias 是一個好習慣。

其餘

  1. 使用alias時,目錄名後面必定要加"/",否則會認爲是個文件。

  2. alias在使用正則匹配時,location後uri中捕捉到要匹配的內容後,並在指定的alias規則內容處使用。

    location ~ ^/users/(.+\.(?:gif|jpe?g|png))$ {
         alias /data/w3/images/$1;
     }
    複製代碼
  3. alias只能位於location塊中,而root的權限不限於location。

舉個例子

你提供地址,當你在家時:

1.朋友想去找你,看到阿姨,阿姨說在家,那朋友獲得的是你提供的地址+阿姨說的,這就是root,會把兩個地址串起來;
2.班主任想去找你,看到阿姨,阿姨說,你直接跟我說就行了,那班主任獲得的就是阿姨說的,這就是alias,會把location後面配置的路徑棄掉,把當前匹配到的目錄指向到指定目錄;
複製代碼

當你不在家時(動靜分離):

1.(alias)班主任找你的結果不變,結果依然是以阿姨說的爲主;
2.(root)由於root是把連個地址串一塊兒的緣由,這種狀況不太適用,若是非要用root,要在阿姨身上加一個電話,直接call你(軟link)
複製代碼

上傳文件

既然能夠訪問了,那就寫個簡單的HTML上傳文件吧,關於後端的話,原本想用php,畢竟這塊網上例子不少,好比這裏,都有源碼了;

可是呢,畢竟不懂php,即便它是最棒的語言,考慮到後面維護麻煩,仍是選擇用flask吧;

直接經過pip命令進行安裝便可:

pip install flask
複製代碼

官方的一個最簡單示例:

# coding=utf-8
from flask import Flask

app = Flask(__name__)


@app.route("/")
def hello():
    return "Hello Flask!"


if __name__ == "__main__":
    app.run()
    #若是想在公網訪問,就修改以下:
    #app.run((host='0.0.0.0',port=5000,debug=True))
    #直接打開ip:5000就能夠了,記得開放端口權限
複製代碼

image.png-21.7kB

這裏不會詳細介紹flask,感興趣的同窗能夠來官網看看;

對於簡單的上傳,通常只須要3個步驟:

1. 建立上傳表單

<form method="POST" enctype="multipart/form-data">
      <input type="file" name="file">
      <input type="submit" value="Upload">
</form>
複製代碼

2. 獲取文件

當點擊上傳/提交按鈕,要獲取到上傳的文件,經過requests對象中的files就能夠獲取到啦~

file = request.files['file']
複製代碼

3. 保存文件 獲取到文件,接着就是保存了,指定路徑和文件名;

file.save(path + filename)
複製代碼

配置文件

實際在上傳文件的時候,會作下限制,好比限制文件大小、文件夾地址、上傳文件擴展名等,而在實際項目,還會有密鑰、數據庫地址等等,這些都是屬於配置項;

通常有3種方式:

直接寫入腳本

當你腳本是輕量,配置項很少的狀況下,能夠直接寫到腳本里面;

from flask import Flask

app = Flask(__name__)
app.config['name'] = 'jb'
app.config['DEBUG'] = True
app.config['age'] = 18
複製代碼

固然也能夠用字典來簡化代碼:

from flask import Flask

app = Flask(__name__)
app.config.update(
    DEBUG=True,
    name='jb',
    age=18
)
複製代碼

單獨配置文件

這種適用於配置項多離的狀況,能夠建立一個獨立的配置文件,如config.py

name = 'jb'
DEBUG = True
age = 18
複製代碼

而後導入配置:

import config

...
app = Flask(__name__)
app.config.from_object(config)
...
複製代碼

或者:

...
app = Flask(__name__)
app.config.from_pyfile('config.py')
...
複製代碼

不一樣的配置類

當須要多個配置配合,好比測試配置、開發配置、運營配置,這時候就須要在配置文件中建立不一樣的配置類,而後在建立程序實例時引入相應的配置類;

這裏繼續以config.py爲例子,建立一個存儲通用配置的類;

import os
basedir = os.path.abspath(os.path.dirname(__file__))


class BaseConfig:  # 基本配置類
    SECRET_KEY = os.getenv('SECRET_KEY', 'some secret words')
    ITEMS_PER_PAGE = 10


class DevelopmentConfig(BaseConfig):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = os.getenv('DEV_DATABASE_URL', 'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')


class TestingConfig(BaseConfig):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = os.getenv('TEST_DATABASE_URL', 'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')
    WTF_CSRF_ENABLED = False


config = {
    'development': DevelopmentConfig,
    'testing': TestingConfig,

    'default': DevelopmentConfig
}
複製代碼

這裏說明下,上面是把配置寫入系統環境變量,而後使用os模塊的getenv()方法獲取,第二個參數做爲默認值;

經過from_object()方法導入配置:

from config import config  # 導入存儲配置的字典

...
app = Flask(__name__)
app.config.from_object(config['development'])  # 獲取相應的配置類
...
複製代碼

那回到此次的功能上,咱們只須要寫到腳本里面便可;

app.config['UPLOAD_FOLDER'] = os.getcwd()
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
複製代碼

固然還要考慮安全問題,如文件名校驗之類的,具體的話,看源碼:

import os
from flask import Flask, request, url_for, send_from_directory
from werkzeug import secure_filename

# 文件擴展名
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = os.getcwd()+"/file_upload"  #上傳地址
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 #文件大小,單位N


html = ''' <!DOCTYPE html> <title>Upload File</title> <h1>圖片上傳</h1> <form method=post enctype=multipart/form-data> <input type=file name=file> <input type=submit value=上傳> </form> '''

# 檢查文件類型
def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

# 獲取上傳後的文件
@app.route('/uploads/<filename>')
def uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'],
                               filename)

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['file']
        #判斷上傳文件名
        if file and allowed_file(file.filename):
        	# 檢查文件名
            filename = secure_filename(file.filename)


            #若是目錄不存在則建立
            if not os.path.exists(app.config['UPLOAD_FOLDER']):
            	os.mkdir(app.config['UPLOAD_FOLDER'])

            # 保存圖片
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))

            # 獲取url
            file_url = url_for('uploaded_file', filename=filename)
            return html + '<br><img src=' + file_url + '>'
    return html





if __name__ == '__main__':
    app.run(host='0.0.0.0',port=8087,debug=True)
複製代碼

這裏說個小事情,一開始執行上面的代碼,會報錯:

image.png-42.2kB

網上找了下,緣由是在Subline3遇到的都是看似空格實則沒有空格引發的::

解決方法: 就是打開subline的空格製表顯示就能夠清楚的顯示出本身是否真的空格了。

第一次遇到這問題,詳情請點擊這裏查看;

執行腳本,上傳文件,上傳的文件就是在file_upload目錄下的;

image.png-10.1kB

功能是有的,就是界面low了點。。

image.png-6.5kB

身爲一個測試同窗,對UI確定要有點追求,而且但願能夠提供下載,所以就上網找了個插件,點擊這裏,看看github上的截圖:

image.png-388kB

聽漂亮的,這樣上傳就有了進度,而且支持多文件上傳,但依然會有個問題,上傳完去哪裏看?想下載怎麼辦?

image.png-6.8kB

這裏想說一個問題,上面的例子,若是你們有細心看的話,會發現上傳的文件都會去到一個叫file_upload文件夾,上傳是沒問題的,可是下載就有問題了;

jb在下載的時候,無論這個下載地址怎麼拼,頁面都會無情報錯,說這個地址不存在;

可是呢,若是把上傳目錄修改爲static,倒是正常的; 包括若是這個static不在根目錄,也同樣有問題,那爲何當static是根目錄且放到這裏面就這樣?

Flask資源定位是依靠

app = Flask(__name__)
複製代碼

__name__參數(文件名或包名),因此相對定位必定要基於這個文件路徑。

爲何會在static文件夾路徑下會正確**?Flask默認靜態文件在static文件下。**

那若是必定要上傳到file_upload文件夾,怎麼辦?

那就修改的flask默認的static文件夾只須要在建立Flask實例的時候,把static_folderstatic_url_path參數設置爲空字符串便可。

app = Flask(__name__, static_folder='', static_url_path='')
複製代碼

訪問的時候用url_for函數,res文件夾和static文件夾同一級:

url_for('static', filename='res/sheeta.jpg')
複製代碼

最終就成了這樣:

<img src ="/file_upload/a.png">
//或者
<img src = "{{ url_for('file_upload', filename = 'a.png') }}>
複製代碼

good,問題搞定了,體驗下:

image.png-16.7kB
image.png-10.8kB
image.png-50.6kB

搞定,那接着就是生成二維碼;

生成二維碼

支持點擊打開/下載,那若是是上傳apk/ipa那就麻煩了,jenkins那邊,因此仍是但願能生成一個二維碼;

而Python生成二維碼的話,有qrcode,同時也須要處理下二維碼圖片,所以須要安裝qrcodepillow

pip install qrcode
pip install pillow
複製代碼

最簡單的demo:

import qrcode

qr = qrcode.QRCode(version=2, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=1)

qr.add_data("我是jb啊~")
qr.make(fit=True)
img = qr.make_image()
img.save("jb_qrcode.png")
複製代碼

這裏面是有一些參數的,可是邊幅緣由,請各自去了解;

這時候就生成一個二維碼了,可是呢,上面的demo圖片過小了,並且想弄個定製化的二維碼,怎麼破?不細說,直接源碼拿走不謝~

# 生成二維碼
def get_QRCode(filename):

    #檢測目錄的方法,不存在則建立
    checkdir(存放二維碼目錄路徑)

    # 初步生成二維碼圖像
    qr = qrcode.QRCode(
        version=5,
        error_correction=qrcode.constants.ERROR_CORRECT_H,
        box_size=8,
        border=4
    )
    # 二維碼存放的內容,可文案,可連接
    qr.add_data("二維碼路徑")
    qr.make(fit=True)

    # 得到Image實例並把顏色模式轉換爲RGBA
    img = qr.make_image()
    img = img.convert("RGBA")

    # 打開logo文件
    icon = Image.open("定製的logo")

    # 計算logo的尺寸
    img_w,img_h = img.size
    factor = 4
    size_w = int(img_w / factor)
    size_h = int(img_h / factor)

    # 比較並從新設置logo文件的尺寸
    icon_w,icon_h = icon.size
    if icon_w >size_w:
        icon_w = size_w
    if icon_h > size_h:
        icon_h = size_h
    icon = icon.resize((icon_w,icon_h),Image.ANTIALIAS)

    # 計算logo的位置,並複製到二維碼圖像中
    w = int((img_w - icon_w)/2)
    h = int((img_h - icon_h)/2)
    icon = icon.convert("RGBA")
    img.paste(icon,(w,h),icon)

    # 保存二維碼
    img.save(os.path.join(二維碼路徑, filename))
複製代碼

能夠直接拿來複用,須要修改的也就幾個參數,簡單便捷,效果圖以下:

image.png-29.6kB

其餘小系列

在處理過程,遇到一些小問題,簡單羅列下;

中文名稱被吃了

是這樣的,若是一個文件裏面有中文,好比消息.jpg,把這個地址塞到二維碼,而後掃描,會這樣的:

image.png-4.8kB
中文不見了。。若是是英文或字母,都很正常,另外,若是是用瀏覽器掃描,也都沒問題,就微信會這樣;

很簡單,encode下就行了;

編碼:

from urllib.parse import quote
text = quote(text, 'utf-8')
複製代碼

解碼:

from urllib.parse import unquote
text = unquote(text, 'utf-8')
複製代碼

這樣後,就能在微信打開啦~

ios不能直接下載ipa包

來到這裏,主路徑都是通的,二維碼也是能夠生成的,用android手機試下,沒問題,能夠在線預覽圖片或者下載文件;

可是用ios手機掃描ipa包的連接,結果不會像安卓那樣下載ipa包安裝,而是load半天,而後這樣顯示:

image.png-372.6kB

???這跟劇本不同啊;

網上找了下,不少這樣的例子,這裏參考的是這裏

大體的步驟就是:

  • 準備plist、ipa包、icon、HTMLdemo;
  • plist上傳到https地址;
  • HTML裏面加一個點擊事件;

原理:

是經過Safari解析連接中的"itms-services://"來實現的。

例如:
<a title="iPhone" href="itms-services://?action=download-manifest&url=https://192.168.10.193/installIPA.plist"> Iphone Download</a>

Safari會去讀取installIPA.plist中的信息,如:iOS應用的名稱、版本、安裝地址等。
複製代碼

test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/layer/2.3/layer.js"></script>
    <title>File Manager</title>
</head>
<body>
<h1>File Manager</h1>
<div id="btnContainer">
        <a id="btnA" href="itms-services://?action=download-manifest&url=你的plist地址,必需要https,否則會提示簽名無效">
            <span id="btnSpan2">v1.1.2</span>
        </a>
 </div>
</html>
複製代碼

這裏的href是填入plist的地址,必需要https,緣由是iOS7.1 之後, Apple再也不支持HTTP方式的OTA ,因此須要開啓HTTPS服務,否則會提示無效證書;

test.plist

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">
  <dict>
    <key>items</key>
    <array>
      <dict>
        <key>assets</key>
        <array>
          <dict>
            <key>kind</key>
            <string>software-package</string>
            <key>url</key>
            <string>ipa包地址</string>
          </dict>
        </array>
        <key>metadata</key>
        <dict>
          <key>bundle-identifier</key>
          <string>包名</string>
          <key>bundle-version</key>
          <string>版本號</string>
          <key>kind</key>
          <string>software</string>
          <key>subtitle</key>
          <string>應用名稱</string>
          <key>title</key>
          <string>應用名稱</string>
        </dict>
      </dict>
    </array>
  </dict>
</plist>
複製代碼

因篇幅問題,只留必備項,只輸入填入ipa包地址,底部那塊元數據信息便可;

最終的結果就是這樣啦:

image.png-175.5kB

主流程總算通了,深呼一口氣;

這裏說明下,若是沒有https,測試的話,能夠試試上傳的github,這裏是https的;

image.png-25.5kB

當長期考慮,仍是要弄一個,公司有就用公司的,公司沒有就本身買一個,jb是用aly,因此也在aly買了個,有興趣的同窗點擊這裏 ,購買、認證、解析、申請證書,就能夠了,這塊不說明,感興趣的可自行上網查詢,

這裏演示的是demo,所以url都是hardcore的,實際還要處理plist的路徑等,上傳一個ipa就生成一個plist,這塊自行處理吧;

源碼

連接:https://pan.baidu.com/s/1SFFtGJHmUHhpqq2MwwhpXw 
提取碼:2hdd 
複製代碼

源碼在此,就再也不單獨解釋了,可能會有一些問題,可是模型大體就這樣啦,因時間問題,年後再優化,反正就是缺什麼就import什麼就行了;

小結

本文折騰好久,主要是由於從新看回flask跟學習下ng,以及ios的解決方案,外加年末工做很繁忙,所以陸陸續續折騰了快半個月的時間,原本還想把全部優化都作好再放出來,但怕開年後更加忙了,那這文章就爛尾了,所以就先發出來了;

本文涉及到的內容比較多,包括jenkins如何顯示圖片pgy分發平臺的使用本身搭一個文件上傳的輪子,涉及到的只是有ngflask,都是比較簡單的內容,可是是否作過是兩回事,從小白的角度出發來落地;

體驗地址點擊這裏,若是服務器沒掛的話,打開是這樣的;

若是有更好的方案,歡迎一塊兒交流~

最後,你們新年快啦~一帆風順,明年見~

最後的最後,謝謝你們~

1-140R3154U8.jpg-9kB

這裏還有一節,主要是介紹下jenkins的內置變量,感興趣的同窗能夠看看~

jenkins內置變量

郵件的配置變量

變量名 說明
${GIT_BRANCH} build 的 Git 分支;
${FILE,path="xxx"} xxx 爲指定的文件,文件內容能夠在郵件中顯示。注意:xxx 是工做區目錄的相對路徑;
${JOB_DESCRIPTION} 顯示項目描述;
${BUILD_NUMBER} 顯示當前構建的編號;
${SVN_REVISION} 顯示 svn 版本號;
${CAUSE} 顯示誰、經過什麼渠道觸發此次構建;
${CHANGES} 顯示上一次構建以後的變化;
${BUILD_ID} 顯示當前構建生成的ID;
${PROJECT_NAME} 顯示項目的全名;
${PROJECT_DISPLAY_NAME} 顯示項目的顯示名稱;
${JENKINS_URL} 顯示 Jenkins 服務器的 url 地址(能夠在系統配置頁更改);
${BUILD_LOG_MULTILINE_REGEX} 按正則表達式匹配並顯示構建日誌;
${BUILD_LOG} 顯示最終構建日誌;
${PROJECT_URL} 顯示項目的URL地址;
${BUILD_STATUS} 顯示當前構建的狀態(失敗、成功等等);
${BUILD_URL} 顯示當前構建的URL地址;
${CHANGES_SINCE_LAST_SUCCESS} 顯示上一次成功構建以後的變化;
${CHANGES_SINCE_LAST_UNSTABLE} 顯示顯示上一次不穩固或者成功的構建以後的變化;
${ENV} 顯示一個環境變量;
${FAILED_TESTS} 若是有失敗的測試,顯示這些失敗的單元測試信息;
${PROJECT_URL} 顯示項目的 URL;
${TEST_COUNTS} 顯示測試的數量;

環境變量

變量名 說明
BRANCH_NAME 設置爲正在構建的分支的名稱;
CHANGE_ID 更改ID,例如拉取請求號;
CHANGE_URL 設置爲更改URL;
CHANGE_TITLE 設置爲更改的標題;
CHANGE_AUTHOR 設置爲擬議更改的做者的用戶名;
CHANGE_AUTHOR_DISPLAY_NAME 設置爲做者的人名;
CHANGE_AUTHOR_EMAIL 設置爲做者的電子郵件地址;
CHANGE_TARGET 設置爲能夠合併更改的目標或基本分支;
BUILD_NUMBER 目前的編號,如「153」;
BUILD_ID 當前版本ID,與BUILD_NUMBER相同;
BUILD_DISPLAY_NAME 當前版本的顯示名稱;
JOB_NAME 此構建項目的名稱,如「foo」;
JOB_BASE_NAME 此創建項目的名稱將剝離文件夾路徑,例如「bar」。
BUILD_TAG jenkins- JOBNAME− {BUILD_NUMBER}的字符串;
EXECUTOR_NUMBER 識別執行此構建的當前執行程序(在同一臺計算機的執行程序中)的惟一編號;
NODE_NAME 代理的名稱;
NODE_LABELS 空格分隔的節點分配的標籤列表;
WORKSPACE 分配給構建做爲工做區的目錄的絕對路徑;
JENKINS_HOME Jenkins主節點上分配的目錄絕對路徑存儲數據;
JENKINS_URL 完整的Jenkins網址,如http://server:port/jenkins/
BUILD_URL 此構建的完整URL,如http://server:port/jenkins/job/foo/15/
JOB_URL 此做業的完整URL,如http://server:port/jenkins/ job/foo/
SVN_REVISION Subversion版本號,當前已被檢出到工做區,如「12345」;
SVN_URL 當前已經檢出到工做空間的Subversion URL;
相關文章
相關標籤/搜索