preface

There e is no smoke without fire, why will do this thing, from a few days ago.

The tragedy

Small companies don’t have the resources, because a lot of internal testing is done by third parties, and dandelion is used here;

In the morning of one day, when developing and testing, packaging, uploading PGY, and preparing for the experience of the business side, the product pops up by clicking the download page button;

At first I thought it was my hand error, and then upload a few times, still show this interface, there is no error message, Meng force ah, before all the good, what ghost?

Toss for a long time, hopeless, pick up the phone, see a message, point open, show this:

It says that financial applications are no longer accepted for distribution on this platform. Although our products are information products, the content is indeed related to finance, which seems to be fine.

It’s a little tricky to operate

The package product of a project of the company is a ZIP, which compresses APK and IPA into a ZIP output after the package is completed. Users need to download the ZIP, decompress it, and install it on their computer with mobile phone or simulator before they can experience it.

The whole link is too long and cumbersome, so I think about two points:

  • Packaging split, support android, ios packaging separately, otherwise sometimes verify the problem of a platform need to pack two packages, packaging time cost problem;
  • Package products display TWO-DIMENSIONAL code for easy download

Before we get started, testerhome actually has a similar article, as shown below:

The corresponding articles are written very well, but the wheel, or to be impressed, and will be corresponding to the lack of content of the corresponding article to complement, as far as possible more detailed to write out the process;

Jenkins display picture

I won’t tell you what Jenkins is or how to install it, but if you have any questions, please click here or in the second place;

The desired effect is this:

  • You can modify the file description
  • Support to display TWO-DIMENSIONAL code

Plug-in installation

Jenkins does not support the above two operations, so you need to install plug-ins to use them.

  • Build Name Setter, which modifies the Build Name
  • Description setter, used to modify Build description, add QRCode (QRCode) to description

Search for the above plugins directly from Jenkins’ Plugin management page and click Install

How to use

Click the setting interface of the corresponding job.

Build Name Setter

#${BUILD_NUMBER}jbtest = #${BUILD_NUMBER}

description setter

However, after filling in, it was found to be different from what was expected. This was because Jenkins adopted Plain text mode for the default Markup Formatter of all description information for the consideration of security. In this mode, the HTML encoding in build description information would not be analyzed.

This is also easy to do by managing Jenkins -> Configure Global Security and changing the Markup Formatter Settings to Safe HTML.

< 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;" >Copy the code

After saving, execute the task, will display the url corresponding picture;

The Jenkins display image problem is solved

Small nodules

Jenkins displays pictures and modifies task description. Two plug-ins need to be installed and an IMAGE IMG tag needs to be uploaded.

Jenkins task results collection products

If a job is created, the apK file will not be displayed by default.

So how do I make it appear on the right hand side? Open the Job option, Post Build Actions, select Archives Build artifacts, and enter the content in Files to Archive.

When locating files, regular expressions can be used to match, or project environment variables can be called. Multiple files are separated by commas.

${OUTPUT_FOLDER}/*.ipa,*.txt, qrcode. PNGCopy the code

The configuration page after addition is as follows:

Rebuild the task to see the corresponding product;

Distribution platform

First of all, non-advertising stickers, non-advertising stickers, this section will not only introduce the distribution platform, but also introduce how to do not use the distribution platform, you can choose;

I searched the Internet and found that the most famous distribution platforms in China are dandelion and FIR. Im

The dandelion

Click the address above to open the official website, register and log in, click the document, there will be API description;

The focus of this chapter is to upload APP. You can search the box to upload APP, or click the link to jump directly.

Look at the response carefully, there is a TWO-DIMENSIONAL code address, good, it is you;

General Parameter Description

parameter Nickname (s) instructions
_api_key API Key API Key: identifies the API caller. Unless otherwise specified, each interface must contain this parameter. For registered users of the same dandelion, this value is fixed;
userKey User Key User Key, which identifies the identity of the current user. For registered users of the same dandelion, this value is fixed.
appKey App Key Represents a unique Key for an App group. For example, if the App named ‘Wechat’ has uploaded three versions, the three versions are one App group, and this parameter indicates the Key of the group. This value is displayed in App Details, App Overview, and App Key.
buildKey Build Key Build Key is the index ID that uniquely identifies the App and can be obtained by obtaining all versions of the App

_API_key and userKey can be obtained by clicking the button of the web page under login state.

Upload the App

Too many parameters, lazy paste, directly on the code;

Linux

Do you want to delete something with 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/uploadCopy the code

Wait until the upload is complete:

buildQRCodeURL

Python environment, Py3


import requests
import sys
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

upload_url = "https://www.pgyer.com/apiv2/app/upload"
_api_key = "Your key"
apk_path = "Path to the file you want to upload."
def pgy_uploadFile():
    Get the parameters passed by the run
    # _apk_path = sys.argv[1]
    # upload the 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("Upload failed, status code:"+req.status_code)
    except Exception as e:
        print("upload:" + e)
if __name__ == '__main__':
    pgy_uploadFile()
Copy the code

If you need to pass an argument to a script, use sys.argv to get it.

At the end, it outputs the QR code address, which you can send to Jenkins.

Hang out with Jenkins

As mentioned above, Jenkins used IMG SRC to display the QR code, but the address of the qr code returned by the dandelion is different every time. What’s the matter? Normally, the SRC value should be written as a variable;

In fact, it is written as a variable, but also because the URL itself changes every time, so can not directly paste the URL, but the URL download down, fixed down a name, the variable directly take the path can be;

The img tag will look like this:

<img src="${BUILD_URL}/artifact/QRCode.png" style="background-color: width: 286px; height: 189px;" > < span style = "max-width: 100%; clear: both;Copy the code

${BUILD_URL} = ${BUILD_URL};

${BUILD_URL} is a Jenkins built-in variable, representing the URL address of the current build. The common Jenkins variables are listed at the end of the article.

For example, if the link of the two-dimensional code is like this:

jenkineUrl/job/jobName/34/artifact/QRCode.png
Copy the code

So, jenkineUrl/job/jobName / 34 this string is ${BUILD_URL}

Since we need the image, let’s download the image, we already have the URL;

def pgy_doanloadQRCode(QRCodeURL):
    print("Ready to download qr code")
    filename = os.getcwd()+"/QRCode.png"
    with open(filename, 'wb') as f:
        Build a new file locally in binary write mode
        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 download completed" % filename)
Copy the code

Results the screenshot

Because PGY needs to upload APK or IPA, because for convenience, direct hardcore image URL to demonstrate the results;

fir.im

Users with real names are allowed 100 free downloads per day, while those without real names are allowed only 10 free downloads per day.

In general, for small businesses, 100 times a day is enough, unless there is a lot of product, or packaging is frequent;

Real name good trouble, but also hold a certificate, it doesn’t matter, anyway, 10 times, enough;

Then go to the API article, gee, response doesn’t have a QR code field? Then manually upload an application to try, results.

Small nodules

I found a distribution platform online, and only dandelion and fir. Im are famous in China, but FIR. Im needs real name to play, so dandelion is left.

To build the wheels

At this time, some students may have questions, our company’s products are confidential, do not want to use a third party, can you make a wheel?

That’s a very good question. Yes, you can. Think about it briefly. What does this wheel need?

  • An interface that provides a button to upload files;
  • The file can be downloaded by clicking, and the two-dimensional code can be displayed when the mouse moves to the file.
  • A server;

The interface is very simple. At first, I was thinking of installing PHPStudy, but suddenly I remembered that a few days ago, I saw a screenshot posted by the big guy:

Thinking about the same, also incidentally lane a warehouse bai, more convenient;

But JB doesn’t know how to do this, so he goes to ask the boss.

Listen to the boss said very simple, but ah, JB only heard ng, did not really play ah, where simple, cry;

So go to a simple understanding of ng, the long road of life;

An introduction to Nginx

You may not have heard of Nginx, but Apache certainly has. Both are HTTP servers, so Nginx is also an open source HTTP server software.

Mainly for what?

  • The reverse proxy
  • Load balancing
  • HTTP server (including static/static separation)
  • Forward agent

The reverse proxy

Reverse proxy is probably one of the most popular things Nginx does;

What is a reverse proxy? Here’s what Baike says:

In Reverse Proxy mode, a Proxy server receives Internet connection requests, forwards the requests to the Intranet server, and returns the results to the Internet client. In this case, the proxy server acts as a reverse proxy server. In simple terms, the real server cannot be directly accessed by the external network, so a proxy server is required. The proxy server can be accessed by the external network and resides on the same network environment as the real server, of course, it may be the same server with different ports.Copy the code

A simple reverse proxy code is posted below

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

        location / {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host:$server_port; }}Copy the code

After saving the configuration file, start Nginx so that when we access localhost, it is equivalent to accessing localhost:8080;

Load balancing

Load balancing is distributed among multiple operation units, such as Web server, FTP server, enterprise critical application server, and other critical task servers, to jointly complete tasks.

In simple terms, when two or more servers are deployed, requests are randomly distributed to the specified server for processing based on rules. In load-balancing configuration, a reverse proxy must be configured at the same time to switch to load balancing.

The HTTP server

Nginx itself is also a static resource server, when only static resources, you can use Nginx to do the server, but now also very popular static separation, you can use Nginx to implement, first look at Nginx to do the static resource server;

server { listen 80; server_name localhost; client_max_body_size 1024M; location / { root e:\wwwroot; index index.html; }}Copy the code

By default, if you visit http://localhost, you will go to index.html in the wwwroot directory on the E disk. If a site is a static page, you can deploy it this way.

Dynamic and static separation

Dynamic and static separation is to make dynamic web pages in the dynamic website according to certain rules to differentiate between constant resources and often changed resources. After dynamic and static resources are split, we can do cache operation according to the characteristics of static resources. This is the core idea of static site processing;

upstream test{  
       server localhost:8080;  
       server localhost:8081;  
    }   
      
    server {  
        listen       80;  
        server_name  localhost;  
  
        location / {  
            root   e:\wwwroot;  
            index  index.html;  
        }  
          
        All static requests are handled by nginx and stored in HTML
        location ~ \.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {  
            root    e:\wwwroot;  
        }  
          
        All dynamic requests are forwarded to Tomcat for processing
        location ~ \.(jsp|do)$ {  
            proxy_pass  http://test; } error_page 500 502 503 504 /50x.html; location = /50x.html { root e:\wwwroot; }}Copy the code

This allows you to place HTML, images, CSS, and JS in the wwwroot directory, while Tomcat handles only JSPS and requests.

For example when we suffix for GIF, Nginx default will obtain dynamic image file to the current request from below returns, of course, that is static files with Nginx is the same server, we can also in another server, and then through the reverse proxy and load balance configuration in the past, as long as to clear up the basic process, A lot of configuration is simple, and localtion is actually a regular expression behind it, so it’s very flexible.

Forward agent

To get content from the original server, the client sends a request to the agent and specifies the destination (the original server). The agent then forwards the request to the original server and returns the content to the client. The client can use the forward proxy.

Nginx common commands

Start the nginx

service nginx start
Copy the code

Stop nginx

nginx -s stop
Copy the code

View the nginx process

ps -ef | grep nginx 
Copy the code

Smooth start nginx

nginx -s reload
Copy the code

Smooth start means restarting nginx, reloading the configuration file, starting the new worker thread, and perfectly stopping the old worker thread without stopping nginx.

Forcibly stop nginx

pkill -9 nginx
Copy the code

Check that the changes to the nginx.conf file are correct

nginx -t -c /etc/nginx/nginx.conf
or 
nginx -t
Copy the code

View the nginx version

nginx -v
or
nginx -V
Copy the code

The port is open

Nginx 1.6 is installed on Aliyun by default.

Directly find the security group rules in Ali Cloud, add the corresponding peer, you can use public IP access;

Modifying the Default Port

Nginx uses port 80 by default. If you need to change it, you need to go to the configuration file to change it.

Nginx installation files in the/etc/nginx, open and found inside a nginx. Conf, check and found no port information, but the last line insert *. The conf file, then we will follow this directory to find;

CD to conf.d, find only one default.conf file in it, edit and check, find a listen port in it, this is it, modify the port required for imaging, save;

Then run the nginx -s reload command to restart the server, and then use the public IP address + port to access the server, the Welcome to nginx!

Increase the port

Some students may ask, can you add more ports?

Conf file and add a new one to the original 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;   # enable nginx directory browsing
        autoindex_exact_size off;   File sizes are displayed starting from KB
        autoindex_localtime on;   # display file modification time to the server local time

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

Copy the code

autoindex

Nginx has a directory browse function (autoindex), but by default it does not allow you to list the entire directory.

The autoindex. HTML file above can be downloaded by clicking the distribution link;

Link: https://pan.baidu.com/s/1oiukkMAILzq9lHwCKzy-0w extraction code: 7 ytcCopy the code

Finally, the overall effect is as follows:

Can also parse readme. md, SAO ah;

root&alias

I have seen others do this when working with ng configuration files.

location /ware {
    alias /lvdata/warehouse/;
Copy the code

And that’s basically what I do:

location / {  
    root   e:\wwwroot;  
Copy the code

I thought, what’s the difference?

Introduction to the

Nginx specifies file paths in two ways: root and alias.

The main difference between root and alias is how Nginx interprets the URI after location, which allows each to map requests to the server file in a different way.

grammar

The use of the root

Syntax: root path; Default: root HTML; Context: HTTP, server, location, if in locationCopy the code

Example 1:

location ^~ /request_path/dirt/ {
    root /local_path/dirt/;
  }
Copy the code

When a client requests /request_path/image/file.ext, Nginx maps the request resolution to /local_path/dirt/request_path/dirt/file.ext

Example 2:

location ^~ /t/ {
 root /www/root/html/;
}
Copy the code

If the URI of a request is /t/a.html, the Web server will return the/WWW /root/ HTML /t/a.html file on the server. The use of the alias

Syntax: alias path; Default: - Context: locationCopy the code

Example 1:

location /request_path/dirt/ {
    alias /local_path/dirt/file/;
}
Copy the code

When a client requests /request_path/dirt/file.ext, Nginx maps the request to /local_path/dirt/file/file.ext Because alias discards the path configured after location (e.g. /request_path/dirt/one.html), it points the currently matched directory to the specified directory.

Example 2:

location ^~ /t/ {
 alias /www/root/html/new_t/;
}
Copy the code

If the URI of a request is /t/a.html, the Web server will return the file/WWW /root/ HTML /new_t/a.html on the server.

Comprehensive example

location /abc/ {
    alias /home/html/abc/;
}
Copy the code

In this configuration, http://test/abc/a.html specifies /home/html/abc/a.html;

This configuration can also be changed to use the root tag:

location /abc/ {
    root /home/html/;
}
Copy the code

In this case, nginx will look for the ABC directory in /home/html/ and get the same result.

However, if you change the alias configuration to

location /abc/ {
    alias /home/html/def/;
}
Copy the code

Then nginx will fetch data directly from /home/html/def/, such as http://test/abc/a.html pointing to /home/html/def/a.html;

This configuration cannot be directly configured using root. If necessary, create a def-> ABC soft link under /home/html/.

In general, it is a good practice to configure root in location/and alias in location /other.

other

  1. When using alias, the directory name must be followed by a slash (/); otherwise, it will be considered a file.

  2. Alias When regular matching is used, the content to be matched is captured in the URI after the location and used in the specified alias rule content.

    location ~ ^/users/(.+\.(? :gif|jpe? g|png))$ { alias /data/w3/images/$1; }Copy the code
  3. Alias can only be in the Location block, and root’s permissions are not limited to location.

For example

You provide the address, while you are at home:

1. Your friend wants to visit you and sees your aunt, but your aunt says you are at home. Your friend gets the address you provided + the address your aunt said, which is root, and will string the two addresses together. This is alias. It will discard the path configured after the location and point the currently matched directory to the specified directory.Copy the code

When you are not at home:

1. The result of the head teacher looking for you remains the same, the result is still based on the aunt said; If you want to use root, you need to add a phone number on your aunt to call you directly.Copy the code

Upload a file

Since you can access it, then write a simple HTML upload file, about the back end, ORIGINALLY wanted to use PHP, after all, this online examples are many, such as here, have source code;

However, I don’t know PHP, so I’ll use flask even if it’s the best language.

Install with PIP command:

pip install flask
Copy the code

The simplest example is official:

# coding=utf-8
from flask import Flask

app = Flask(__name__)


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


if __name__ == "__main__":
    app.run()
    If you want to access the network from the public network, modify the following:
    # app. The run ((host = '0.0.0.0', the port = 5000, debug = True))
    Open IP address :5000, remember to open port permission
Copy the code

Flask won’t be covered in detail here, but you can check out the flask website;

For simple uploads, there are generally only 3 steps:

1. Create an upload form

<form method="POST" enctype="multipart/form-data">
      <input type="file" name="file">
      <input type="submit" value="Upload">
</form>
Copy the code

2. Obtain files

To retrieve the uploaded file when you click the Upload/Submit button, use files in the Requests object

file = request.files['file']
Copy the code

3. Save the file. If the file is saved, specify the path and file name.

file.save(path + filename)
Copy the code

The configuration file

In fact, when uploading files, restrictions will be made, such as file size, folder address, upload file extension, and so on. In actual projects, there will also be keys, database address, and so on, which are configuration items.

Generally, there are three ways:

Write directly to script

When your script is lightweight and has few configuration items, you can write directly to the script.

from flask import Flask

app = Flask(__name__)
app.config['name'] = 'jb'
app.config['DEBUG'] = True
app.config['age'] = 18
Copy the code

You can also use dictionaries to simplify code:

from flask import Flask

app = Flask(__name__)
app.config.update(
    DEBUG=True,
    name='jb',
    age=18
)
Copy the code

Individual configuration file

This applies to multiple configuration items. You can create a separate configuration file such as config.py.

name = 'jb'
DEBUG = True
age = 18
Copy the code

Then import the configuration:

import config

...
app = Flask(__name__)
app.config.from_object(config)
...
Copy the code

Or:

. app = Flask(__name__) app.config.from_pyfile('config.py')...Copy the code

Different configuration classes

When multiple configurations are required, such as test configuration, development configuration, and operation configuration, you need to create different configuration classes in the configuration file, and then introduce the corresponding configuration classes when creating the program instance.

Continuing with the config.py example, create a class that stores the generic configuration;

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


class BaseConfig:  Basic configuration classes
    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
}
Copy the code

Write the configuration to the system environment variable, and then use the OS getenv() method to get the second parameter as the default value.

Import the configuration through the from_object() method:

from config import config  Import the dictionary that stores the configuration. app = Flask(__name__) app.config.from_object(config['development'])  Get the corresponding configuration class.Copy the code

So going back to the functionality this time, we just need to write it into the script;

app.config['UPLOAD_FOLDER'] = os.getcwd()
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
Copy the code

Of course, there are also security issues to consider, such as file name verification and so on, specifically, look at the source code:

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

# File extension
ALLOWED_EXTENSIONS = set(['png'.'jpg'.'jpeg'.'gif'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = os.getcwd()+"/file_upload"  # upload address
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 The size of the file is N


html = ' ''
       Upload File 

Upload image

< form type=submit value >
'
' ' Check the file type def allowed_file(filename): return '. ' in filename and \ filename.rsplit('. '[1], 1)in ALLOWED_EXTENSIONS Get the uploaded file @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'] # Determine the name of the upload file if file and allowed_file(file.filename): # check file name filename = secure_filename(file.filename) Create a directory if it does not exist if not os.path.exists(app.config['UPLOAD_FOLDER']): os.mkdir(app.config['UPLOAD_FOLDER']) # Save images file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) # get 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) Copy the code

Here’s a small thing: the first time you execute the above code, you get an error:

Subline3 seems to have no space, but there is no space.

Solution: Turn on subline’s space TAB and you’ll see if you’re really blank.

This is the first time I encountered this problem. Please click here for details.

Execute the script, upload the file, upload the file is in the file_upload directory;

The e function is some, is the interface low point.

As a test student, I must have a little pursuit of UI, and hope to provide download, so I went online to find a plug-in, click here, see the screenshot on Github:

Listen to beautiful, so upload progress, and support multiple file upload, but there will still be a problem, upload where to see? What if you want to download it?

In the example above, if you look carefully, you will find that the uploaded files will go to a folder called file_upload. Uploading is ok, but downloading is not.

No matter how the download address is spelled, the page will report an error saying that the address does not exist.

However, it is normal to change the upload directory to static; If static is not in the root directory, then why is static in the root directory?

Flask resource location is relied on

app = Flask(__name__)
Copy the code

The __name__ argument (filename or package name), so relative location must be based on the file path.

Why is the static folder path correct **? Flask static files are in static files by default. **

What if you must upload to the file_upload folder?

The default static folder for flask is simply set to empty string values for the static_folder and static_url_path parameters when the flask instance is created.

app = Flask(__name__, static_folder='', static_url_path='')
Copy the code

Use the url_for function when accessing the static directory:

url_for('static', filename='res/sheeta.jpg')
Copy the code

It ended up like this:

< img SRC = "/ file_upload/Amy polumbo ng" > / / or < img SRC = "{{url_for (' file_upload, filename = 'Amy polumbo ng)}} >Copy the code

Good, problem solved, experience:

Ok, so that’s the next generation of qr codes;

Generate qr code

Click open/download is supported, so if it is to upload APK/IPA, it will be troublesome, Jenkins side, so we still hope to generate a TWO-DIMENSIONAL code;

However, if Python generates the TWO-DIMENSIONAL code, it has qrcode, and also needs to process the two-dimensional code pictures, so it needs to install qrcode and Pillow:

pip install qrcode
pip install pillow
Copy the code

The simplest demo:

import qrcode

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

qr.add_data("I'm jb.")
qr.make(fit=True)
img = qr.make_image()
img.save("jb_qrcode.png")
Copy the code

There are some parameters in this, but the margin reason, please understand separately;

At this time to generate a TWO-DIMENSIONAL code, but, the demo picture above is too small, and want to get a customized two-dimensional code, how to break? Don’t elaborate, direct source code take not thank ~

# Generate qr code
def get_QRCode(filename):

    Create a directory if it does not existCheckdir (Directory path for storing TWO-DIMENSIONAL code)# Initial generation of TWO-DIMENSIONAL code image
    qr = qrcode.QRCode(
        version=5,
        error_correction=qrcode.constants.ERROR_CORRECT_H,
        box_size=8,
        border=4
    )
    # Two-dimensional code stored content, copywriting, can link
    qr.add_data("Two-dimensional code path")
    qr.make(fit=True)

    Get the Image instance and convert the color mode to RGBA
    img = qr.make_image()
    img = img.convert("RGBA")

    # Open the logo file
    icon = Image.open("Custom logo")

    # Calculate the size of the logo
    img_w,img_h = img.size
    factor = 4
    size_w = int(img_w / factor)
    size_h = int(img_h / factor)

    Compare and resize the logo file
    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)

    # Calculate the position of the logo and copy it into the QR code image
    w = int((img_w - icon_w)/2)
    h = int((img_h - icon_h)/2)
    icon = icon.convert("RGBA")
    img.paste(icon,(w,h),icon)

    # Save qr codeImg.save (os.path.join(qr code path, filename))Copy the code

Can be directly reused, need to modify only a few parameters, simple and convenient, the effect diagram is as follows:

Other small series

In the process of processing, encountered some small problems, simple list;

The Chinese name was eaten

Here’s the thing: If a file has Chinese characters in it, like message.jpg, insert the address into the QR code and scan it, it looks like this:

It’s very simple, just encode;

Code:

from urllib.parse import quote
text = quote(text, 'utf-8')
Copy the code

Decoding:

from urllib.parse import unquote
text = unquote(text, 'utf-8')
Copy the code

After doing this, you can open it on wechat

Ios cannot directly download ipA packages

Here, the main path is through, qr code can be generated, with an Android phone to try, no problem, you can online preview pictures or download files;

However, if you use an ios phone to scan the link of ipA package, you will not download the IPA package like Android, but load it for half a day, and then display it like this:

????? That’s not the script;

I looked on the Internet, a lot of examples of this, and I refer to this,

The general steps are:

  • Prepare PLIST, IPA package, Icon, HTMLdemo;
  • Plist uploads to HTTPS address;
  • Add a click event to the HTML;

Principle:

This is done by Safari parsing "ITMS-services ://" in the link. <a title="iPhone" href="itms-services://? Action = download - manifest&url = https://192.168.10.193/installIPA.plist "> Iphone download < / a > Safari reads installipa. plist information, such as the name, version, and installation address of the iOS app.Copy the code

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 = Your PList address must be HTTPS or you will be told your signature is invalid.">
            <span id="btnSpan2"> v1.1.2 < / span > < / a > < / div > < / HTML >Copy the code

The href is the plist address, which must be HTTPS. The reason is that after iOS7.1, Apple no longer supports OTA over HTTP. Therefore, you need to enable the HTTPS service; otherwise, an invalid certificate will be displayed.

test.plist

<? xml version="1.0" encoding="utf-8"? > <! DOCTYPE plist PUBLIC"- / / / / DTD PLIST Apple 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 package address </string> </dict> </array> <key>metadata</key> <dict> <key>bundle-identifier</key> <string> Package name </string> <key>bundle-version</key> <string> Version number </string> <key>kind</key> <string>software</string> <key>subtitle</key> <string> Application name </string> <key> Title </key> <string> Application name </string> </dict> </dict> </array> </dict> </plist>Copy the code

Due to space problems, only the necessary items, only enter the IPA packet address, the bottom piece of metadata information can be;

The end result is this:

The main process finally through, deep breath;

Note that if you don’t have HTTPS, you can try uploading to Github, which is HTTPS;

Jb uses ALY, so WE also bought one from ALY. Interested students click here to purchase, authenticate, analyze and apply for certificate. This does not explain, interested students can query online.

This is a demo, so the URL is hardcore. In fact, you need to handle the path of plist, etc. Upload an IPA to generate a PList.

The source code

Link: https://pan.baidu.com/s/1SFFtGJHmUHhpqq2MwwhpXw Numbers: 2 HDDCopy the code

Source code here, will not be explained separately, there may be some problems, but the model roughly so, because of the time problem, after the optimization, anyway is missing what import what is good;

summary

This article for a long time, mainly because look back in flask with learning the ng, and ios solutions, along with the end of the work is very busy, so for nearly half a month of time, one was going to be put out again, all optimization more busy, but afraid of years that this article is abandoned, so start came;

This paper involves a lot of content, including Jenkins how to display pictures, the use of pGY distribution platform, build a file upload wheel by myself, involving only NG and flask, are relatively simple content, but whether it is done is two different things, from the perspective of small white landing;

If I click here, if the server is not hung, it opens like this;

If you have a better plan, welcome to communicate with us ~

Finally, happy New Year, everyone. See you next year

Finally, thank you

Here is another section, mainly to introduce Jenkins’ built-in variables, interested students can look at ~

Jenkins built-in variables

The configuration variable of the message

The variable name instructions
${GIT_BRANCH} Git branch of build;
${FILE,path="xxx"} XXX is the specified file whose content can be displayed in the email. Note: XXX is the relative path to the workspace directory;
${JOB_DESCRIPTION} Display project description;
${BUILD_NUMBER} Displays the current build number;
${SVN_REVISION} The SVN version is displayed.
${CAUSE} Show who triggers the build and through what channels;
${CHANGES} Shows changes since the last build;
${BUILD_ID} Displays the ID of the current build generation;
${PROJECT_NAME} Display the full name of the project;
${PROJECT_DISPLAY_NAME} The display name of the display item;
${JENKINS_URL} Display Jenkins server URL (can be changed on system configuration page);
${BUILD_LOG_MULTILINE_REGEX} Match and display the build log by regular expression;
${BUILD_LOG} Display the final build log;
${PROJECT_URL} Display the URL of the project;
${BUILD_STATUS} Displays the status of the current build (failed, successful, and so on);
${BUILD_URL} Displays the URL address of the current build.
${CHANGES_SINCE_LAST_SUCCESS} Shows changes since the last successful build;
${CHANGES_SINCE_LAST_UNSTABLE} Display shows changes since the last unstable or successful build;
${ENV} Display an environment variable;
${FAILED_TESTS} If there are failed tests, display information about those failed unit tests.
${PROJECT_URL} Display the URL of the project;
${TEST_COUNTS} Display the number of tests;

The environment variable

The variable name instructions
BRANCH_NAME Set to the name of the branch being built;
CHANGE_ID Change ID, such as pull request number;
CHANGE_URL Set to change the URL;
CHANGE_TITLE Set to the title of the change;
CHANGE_AUTHOR Set to the username of the author of the proposed change;
CHANGE_AUTHOR_DISPLAY_NAME Set to the author’s name;
CHANGE_AUTHOR_EMAIL Set to the author’s email address;
CHANGE_TARGET Set to a target or base branch where changes can be merged;
BUILD_NUMBER Current numbers, such as “153”;
BUILD_ID Current version ID, same as BUILD_NUMBER;
BUILD_DISPLAY_NAME The display name of the current version;
JOB_NAME The name of the build project, such as “foo”;
JOB_BASE_NAME The name of this build project will strip the folder path, for example “bar”.
BUILD_TAG Jenkins - JOBNAME - {BUILD_NUMBER}String;
EXECUTOR_NUMBER Identifies the unique number of the current executor (within the executor on the same machine) performing this build;
NODE_NAME The name of the agent;
NODE_LABELS A list of labels allocated by space-separated nodes;
WORKSPACE The absolute path to the directory assigned to the build as a workspace;
JENKINS_HOME The absolute path of the directory allocated on Jenkins primary node stores data;
JENKINS_URL The full Jenkins url, e.ghttp://server:port/jenkins/;
BUILD_URL The full URL for this build, as shown inhttp://server:port/jenkins/job/foo/15/;
JOB_URL The full URL for this job, as inhttp://server:port/jenkins/ job/foo/;
SVN_REVISION Subversion version number, currently checked out to workspace, e.g. “12345”;
SVN_URL The Subversion URL is currently checked out to the workspace;