無風不起浪,爲何會作這個事情,就要由前幾天講起了。。 php
小公司沒有資源,由於不少內測都是用第三方的,這邊用的是蒲公英;css
在某日早上,開發提測,打包,上傳pgy
,準備給業務方體驗的時候,結果點擊查看下載頁
按鈕,彈出這貨; html
一開始覺得是本身手誤,而後再上傳幾回,依然顯示這個界面,也沒有任何報錯信息,懵逼啊,以前都用的好好的,什麼鬼? jquery
折騰半天,無望,拿起手機,看到有短信,點開一開,顯示這個: android
這裏面說到再也不接受金融類應用在該平臺分發,我司產品雖然是資訊類產品,但內容的確是金融相關的,好像沒毛病;ios
公司某項目的打包產品是一個zip,是當打包完成後把apk跟ipa壓縮成一個zip輸出,而使用者須要下載這個zip,解壓,電腦連手機/模擬器安裝,方可體驗; nginx
整個鏈路過長,也比較麻煩,所以就想着兩點:git
在正式開始以前,先說明下,testerhome其實有相似的文章,以下圖: github
對應的文章都寫的挺好的,可是輪子嘛,仍是要親力親爲印象才深入,並且會針對對應文章缺少的內容進行相應補充,儘量更加詳細把過程寫出來;web
這裏不會再講述jenkins是什麼,怎麼安裝之類的內容,若是有疑問,請點擊此處或第二處查看;
想要作成的效果是這樣的:
jenkins不支持上面兩個操做的,所以須要安裝插件來使用;
直接在Jenkins的插件管理頁面搜索上述插件,點擊安裝便可
點擊對應job的設置界面;
Build Name Setter
點擊Build Environment
,找到set build name
,默認是#${BUILD_NUMBER}
,這裏能夠自定義,以下修改爲#${BUILD_NUMBER}jbtest
,執行任務後的結果是這樣的;
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
便可。
<img src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3559712731,2278077975&fm=26&gp=0.jpg" style="background-color: rgb(177, 197, 163); width: 286px; height: 189px;">
複製代碼
保存後,執行任務,會就會顯示url對應的圖片了;
到這裏,jenkins上顯示圖片的問題,就這樣解決啦~
jenkins顯示圖片及修改任務描述,須要安裝兩個插件,而且須要傳一個圖片的img標籤過來便可;
這裏額外說起一個點,若是job裏面是有產物的,好比apk等文件,默認構建後是不會顯示出來的,以下圖:
那怎樣讓其在右側顯示出來?仍是打開job的設置項,Post-build Actions
,選擇歸檔成品/Archives build artifacts,在Files to archive
裏面輸入內容就好啦;
定位文件時,能夠經過正則表達式進行匹配,也能夠調用項目的環境變量;多個文件經過逗號進行分隔;
${OUTPUT_FOLDER}/*.ipa,*.txt,QRCode.png
#注意,${OUTPUT_FOLDER}是
複製代碼
添加後的配置頁面以下圖所示:
從新構建任務,就能夠看到對應的產物啦;
首先說明,非廣告貼,非廣告貼,這節除了介紹分發平臺,也會介紹不使用分發平臺時怎麼搞,任君選擇;
上網搜了下,目前國內比較有名且還能用的分發平臺,就是蒲公英跟fir.im
點擊上面的地址打開官網,註冊登陸,點擊文檔,會有API說明;
簡單看了下,支持的功能蠻多的,好像能夠,而本章的重點是上傳APP,能夠搜索框輸入上傳APP,也能夠點擊連接直接跳轉;
仔細看了下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_key
跟userKey
在登陸狀態下,點擊網頁的按鈕便可獲取;
參數太多了,懶的貼了,直接上代碼吧;
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
複製代碼
執行後等待上傳完便可:
從返回的結果來看,是有一個 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就好啦;
上面說起到,jenkins顯示二維碼是利用img src來處理,可是這個蒲公英返回的二維碼地址是每次都不一樣的呢,那怎麼搞?按照常理來講,是把src的值寫成變量就好啦;
其實就是寫成一個變量就行了,可是也由於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來演示結果;
點擊這裏跳轉到官網,看了下,實名認證用戶有 100 次/日的免費下載限額,未實名,僅有10次/日的免費下載限額;
通常來講,小公司,天天100次夠用啦,除非產品夠多,或者打包頻繁;
實名好麻煩,還要手持證件照,不要緊,反正有10次,夠用啦;
而後去看api文章,咦,response竟然沒有二維碼字段?那手動上傳一個應用試試看,結果。。
當時內心的疑問就以下圖同樣,好吧,再見;網上找了下分發平臺,國內比較有名且還能用的只剩下蒲公英跟fir.im,然而fir.im須要實名才能玩,那就剩下蒲公英了,親自接入下蒲公英,接入比較簡單,並且支持的字段也很多,目前來看,比較推薦,省去很多事;
此時有同窗可能會有疑問,我司產品比較機密,不想用第三方,本身能夠造一個輪子嗎?
這個問題很是好,沒錯,能夠的,簡單想一想了,這個輪子須要啥?
想的界面很簡單,原本一開始是想着安裝個phpstudy就好啦,可是忽然想起,前幾天看到大佬發了個截圖:
想着差很少,也順便弄一個倉庫唄,比較方便;
但jb不懂這個怎麼弄,就跑去問老大了;
聽老大說挺簡單的,可是啊,jb只聽過ng,沒真正玩過啊,哪裏簡單的了,哭;
因而乎就去簡單瞭解ng下了,漫漫人生路;
也許沒聽過nginx,可是不要緊,Apache確定是聽過的,這二者都屬於http server,所以,nginx一樣是一款開源的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臺或以上服務器時,根據規則隨機的將請求分發到指定的服務器上處理,負載均衡配置通常都須要同時配置反向代理,經過反向代理跳轉到負載均衡。
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
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訪問啦;
因nginx默認是使用80端口的,若是須要修改,須要去配置文件修改;
nginx安裝文件在/etc/nginx
,打開後發現裏面有個nginx.conf
,查看發現裏面沒有端口信息,可是最後一行插入了*.conf
文件,那咱們就跟着這目錄找;
cd
到conf.d目錄,發現裏面只有一個default.conf
文件,編輯查看,發現裏面有個listen端口,這個就是了,修改爲像要的端口,保存便可;
而後輸入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;
}
複製代碼
nginx有一個目錄瀏覽功能(autoindex),可是呢,默認是不容許列出整個目錄的,若是有需求,就用上面的新增端口的方式來操做就好啦;
而上面這個autoindex.html文件點擊下發連接下載便可;
連接:https://pan.baidu.com/s/1oiukkMAILzq9lHwCKzy-0w
提取碼:7ytc
複製代碼
最後,整個效果以下:
還能夠解析README.md
,騷啊;
在弄ng的配置文件時,看到過別人是這樣弄的;
location /ware {
alias /lvdata/warehouse/;
複製代碼
而本身基本上只會這麼弄的:
location / {
root e:\wwwroot;
複製代碼
當時內心就想,這二者有什麼區別?
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 是一個好習慣。
使用alias時,目錄名後面必定要加"/",否則會認爲是個文件。
alias在使用正則匹配時,location後uri中捕捉到要匹配的內容後,並在指定的alias規則內容處使用。
location ~ ^/users/(.+\.(?:gif|jpe?g|png))$ {
alias /data/w3/images/$1;
}
複製代碼
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就能夠了,記得開放端口權限
複製代碼
這裏不會詳細介紹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)
複製代碼
這裏說個小事情,一開始執行上面的代碼,會報錯:
網上找了下,緣由是在Subline3遇到的都是看似空格實則沒有空格引發的::
解決方法: 就是打開subline的空格製表顯示就能夠清楚的顯示出本身是否真的空格了。
第一次遇到這問題,詳情請點擊這裏查看;
執行腳本,上傳文件,上傳的文件就是在file_upload
目錄下的;
功能是有的,就是界面low了點。。
身爲一個測試同窗,對UI確定要有點追求,而且但願能夠提供下載,所以就上網找了個插件,點擊這裏,看看github上的截圖:
聽漂亮的,這樣上傳就有了進度,而且支持多文件上傳,但依然會有個問題,上傳完去哪裏看?想下載怎麼辦?
這裏想說一個問題,上面的例子,若是你們有細心看的話,會發現上傳的文件都會去到一個叫file_upload
文件夾,上傳是沒問題的,可是下載就有問題了;
jb在下載的時候,無論這個下載地址怎麼拼,頁面都會無情報錯,說這個地址不存在;
可是呢,若是把上傳目錄修改爲static
,倒是正常的; 包括若是這個static
不在根目錄,也同樣有問題,那爲何當static
是根目錄且放到這裏面就這樣?
Flask資源定位是依靠
app = Flask(__name__)
複製代碼
__name__
參數(文件名或包名),因此相對定位必定要基於這個文件路徑。
爲何會在static文件夾路徑下會正確**?Flask默認靜態文件在static文件下。**
那若是必定要上傳到file_upload
文件夾,怎麼辦?
那就修改的flask默認的static文件夾只須要在建立Flask實例的時候,把static_folder
和static_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,問題搞定了,體驗下:
搞定,那接着就是生成二維碼;
支持點擊打開/下載,那若是是上傳apk/ipa那就麻煩了,jenkins那邊,因此仍是但願能生成一個二維碼;
而Python生成二維碼的話,有qrcode,同時也須要處理下二維碼圖片,所以須要安裝qrcode
、pillow
:
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))
複製代碼
能夠直接拿來複用,須要修改的也就幾個參數,簡單便捷,效果圖以下:
在處理過程,遇到一些小問題,簡單羅列下;
是這樣的,若是一個文件裏面有中文,好比消息.jpg
,把這個地址塞到二維碼,而後掃描,會這樣的:
很簡單,encode下就行了;
編碼:
from urllib.parse import quote
text = quote(text, 'utf-8')
複製代碼
解碼:
from urllib.parse import unquote
text = unquote(text, 'utf-8')
複製代碼
這樣後,就能在微信打開啦~
來到這裏,主路徑都是通的,二維碼也是能夠生成的,用android手機試下,沒問題,能夠在線預覽圖片或者下載文件;
可是用ios手機掃描ipa包
的連接,結果不會像安卓那樣下載ipa包安裝,而是load半天,而後這樣顯示:
???這跟劇本不同啊;
網上找了下,不少這樣的例子,這裏參考的是這裏,
大體的步驟就是:
原理:
是經過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
包地址,底部那塊元數據信息便可;
最終的結果就是這樣啦:
主流程總算通了,深呼一口氣;
這裏說明下,若是沒有https,測試的話,能夠試試上傳的github
,這裏是https的;
當長期考慮,仍是要弄一個,公司有就用公司的,公司沒有就本身買一個,jb是用aly,因此也在aly買了個,有興趣的同窗點擊這裏 ,購買、認證、解析、申請證書,就能夠了,這塊不說明,感興趣的可自行上網查詢,
這裏演示的是demo,所以url都是hardcore的,實際還要處理plist
的路徑等,上傳一個ipa
就生成一個plist
,這塊自行處理吧;
連接:https://pan.baidu.com/s/1SFFtGJHmUHhpqq2MwwhpXw
提取碼:2hdd
複製代碼
源碼在此,就再也不單獨解釋了,可能會有一些問題,可是模型大體就這樣啦,因時間問題,年後再優化,反正就是缺什麼就import什麼就行了;
本文折騰好久,主要是由於從新看回flask
跟學習下ng
,以及ios的解決方案,外加年末工做很繁忙,所以陸陸續續折騰了快半個月的時間,原本還想把全部優化都作好再放出來,但怕開年後更加忙了,那這文章就爛尾了,所以就先發出來了;
本文涉及到的內容比較多,包括jenkins如何顯示圖片
、pgy分發平臺的使用
、本身搭一個文件上傳的輪子
,涉及到的只是有ng
、flask
,都是比較簡單的內容,可是是否作過是兩回事,從小白的角度出發來落地;
體驗地址點擊這裏,若是服務器沒掛的話,打開是這樣的;
若是有更好的方案,歡迎一塊兒交流~
最後,你們新年快啦~一帆風順,明年見~
最後的最後,謝謝你們~
這裏還有一節,主要是介紹下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; |