I. Introduction#
Due to the failure of https://www.jsdelivr.com/, the blogger plans to back up the image hosting on GitHub to other platforms.
Seeing Lsky Image Hosting online sparked the idea of building a self-hosted image hosting service, which requires PHP >= 8.0.2, so I planned to compile and install PHP.
Ultimately, I gave up due to the server's low configuration (1 core 2G) causing the compilation to fail. I then turned to the Youpai Cloud platform, where I had already set up the environment.
Blog content: PHP-CentOS8.2 Compile and Install PHP8.1.10
+ Write a script to migrate GitHub image hosting to Youpai Cloud (ftp)
+ Write a script to batch modify article content
II. Main Content#
2.1❤ Compile and Install PHP8.1.10 on CentOS8.2#
2.1.1 Download PHP8 and oniguruma source code and upload it to the server's /mnt directory
https://www.php.net/distributions/php-8.1.10.tar.gz
https://codeload.github.com/kkos/oniguruma/tar.gz/refs/tags/v6.9.4
# Extract
tar xzf oniguruma-6.9.4.tar.gz
tar xzf php-8.1.10.tar.gz
2.1.2 Install PHP8 dependencies
# 2 Install PHP8 dependencies
yum -y install autoconf freetype gd libpng libpng-devel libjpeg libxml2 libxml2-devel zlib curl curl-devel net-snmp-devel libjpeg-devel php-ldap openldap-devel openldap-clients freetype-devel gmp-devel libzip libzip-devel sqlite-devel automake libtool
2.1.3 Compile the PHP8 dependency package oniguruma
# 3.1 Generate configure
cd /mnt/oniguruma-6.9.4
./autogen.sh
# 3.2 Generate compilation configuration file
./configure --prefix=/usr
# 3.3 Compile and install
make && make install
2.1.4 Compile the main PHP8 package
# 4.1 Generate compilation configuration file
cd /mnt/php-8.1.10
./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --enable-fpm --with-fpm-user=nginx --with-fpm-group=nginx --enable-mysqlnd --with-mysqli --with-pdo-mysql --enable-opcache --with-pcre-jit --enable-gd --with-jpeg --with-freetype --with-gettext --with-curl --with-openssl --enable-sockets --enable-mbstring --enable-xml --with-zip --with-zlib --with-snmp --with-mhash --enable-ftp --enable-bcmath --enable-soap --enable-shmop --enable-sysvsem --enable-pcntl --with-gmp
# 4.2 Compile and install
make && make install
Due to low configuration, the compilation failed.
2.1.5 Directory after compilation and installation
/usr/local/php
References
https://www.bilibili.com/read/cv9248283/
2.2 Migrate GitHub Image Hosting to Youpai Cloud via FTP#
References
Official Video Tutorial - Create Storage Service and Use FTP Upload You can get the username and password
The application for Youpai Cloud storage service has been completed + binding a custom domain https://help.upyun.com/knowledge-base/quick_start/
2.2.1 Write a Python script for batch processing
Source from http://blog.csdn.net/ouyang_peng/article/details/79271113
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from cmath import log
from ftplib import FTP
import os
import sys
import time
import socket
import subprocess
class MyFTP:
"""
FTP automatic download and upload script, can operate recursively on directories
Author: Ouyang Peng
Blog address: http://blog.csdn.net/ouyang_peng/article/details/79271113
"""
def __init__(self, host, port=21):
""" Initialize FTP client
Parameters:
host: IP address
port: Port number
"""
# print("__init__()---> host = %s ,port = %s" % (host, port))
self.host = host
self.port = port
self.ftp = FTP()
# Reset encoding
#self.ftp.encoding = 'gbk'
self.ftp.encoding = 'utf8'
# Get script path
path = os.path.dirname(os.path.realpath(__file__))
self.log_file = open(path + "/log.txt", "a", encoding='utf-8')
self.file_list = []
def login(self, username, password):
""" Initialize FTP client
Parameters:
username: Username
password: Password
"""
try:
timeout = 60
socket.setdefaulttimeout(timeout)
# 0 active mode 1 passive mode
self.ftp.set_pasv(True)
# Open debug level 2 to show detailed information
# self.ftp.set_debuglevel(2)
self.debug_print('Starting to connect to %s' % self.host)
self.ftp.connect(self.host, self.port)
self.debug_print('Successfully connected to %s' % self.host)
self.debug_print('Starting to log in to %s' % self.host)
self.ftp.login(username, password)
self.debug_print('Successfully logged in to %s' % self.host)
self.debug_print(self.ftp.welcome)
except Exception as err:
self.deal_error("FTP connection or login failed, error description: %s" % err)
pass
def is_same_size(self, local_file, remote_file):
"""Check if the sizes of the remote file and local file are the same
Parameters:
local_file: Local file
remote_file: Remote file
"""
try:
remote_file_size = self.ftp.size(remote_file)
except Exception as err:
# self.debug_print("is_same_size() error description: %s" % err)
remote_file_size = -1
try:
local_file_size = os.path.getsize(local_file)
except Exception as err:
# self.debug_print("is_same_size() error description: %s" % err)
local_file_size = -1
self.debug_print('local_file_size:%d , remote_file_size:%d' % (local_file_size, remote_file_size))
if remote_file_size == local_file_size:
return 1
else:
return 0
def download_file(self, local_file, remote_file):
"""Download file from FTP
Parameters:
local_file: Local file
remote_file: Remote file
"""
self.debug_print("download_file()---> local_path = %s ,remote_path = %s" % (local_file, remote_file))
if self.is_same_size(local_file, remote_file):
self.debug_print('%s file size is the same, no need to download' % local_file)
return
else:
try:
self.debug_print('>>>>>>>>>>>>Downloading file %s ... ...' % local_file)
buf_size = 1024
file_handler = open(local_file, 'wb')
self.ftp.retrbinary('RETR %s' % remote_file, file_handler.write, buf_size)
file_handler.close()
except Exception as err:
self.debug_print('Error downloading file, exception: %s ' % err)
return
def download_file_tree(self, local_path, remote_path):
"""Download multiple files from the remote directory to the local directory
Parameters:
local_path: Local path
remote_path: Remote path
"""
print("download_file_tree()---> local_path = %s ,remote_path = %s" % (local_path, remote_path))
try:
self.ftp.cwd(remote_path)
except Exception as err:
self.debug_print('Remote directory %s does not exist, continuing...' % remote_path + " ,specific error description: %s" % err)
return
if not os.path.isdir(local_path):
self.debug_print('Local directory %s does not exist, creating local directory first' % local_path)
os.makedirs(local_path)
self.debug_print('Switched to directory: %s' % self.ftp.pwd())
self.file_list = []
# Method callback
self.ftp.dir(self.get_file_list)
remote_names = self.file_list
self.debug_print('Remote directory list: %s' % remote_names)
for item in remote_names:
file_type = item[0]
file_name = item[1]
local = os.path.join(local_path, file_name)
if file_type == 'd':
print("download_file_tree()---> Downloading directory: %s" % file_name)
self.download_file_tree(local, file_name)
elif file_type == '-':
print("download_file()---> Downloading file: %s" % file_name)
self.download_file(local, file_name)
self.ftp.cwd("..")
self.debug_print('Returned to upper directory %s' % self.ftp.pwd())
return True
def upload_file(self, local_file, remote_file):
"""Upload file from local to FTP
Parameters:
local_path: Local file
remote_path: Remote file
"""
if not os.path.isfile(local_file):
self.debug_print('%s does not exist' % local_file)
return
if self.is_same_size(local_file, remote_file):
self.debug_print('Skipping equal file: %s' % local_file)
return
buf_size = 1024
file_handler = open(local_file, 'rb')
self.ftp.storbinary('STOR %s' % remote_file, file_handler, buf_size)
file_handler.close()
self.debug_print('Uploaded: %s' % local_file + " successfully!")
def upload_file_tree(self, local_path, remote_path):
"""Upload multiple files from the local directory to FTP
Parameters:
local_path: Local path
remote_path: Remote path
"""
if not os.path.isdir(local_path):
self.debug_print('Local directory %s does not exist' % local_path)
return
self.ftp.cwd(remote_path)
self.debug_print('Switched to remote directory: %s' % self.ftp.pwd())
local_name_list = os.listdir(local_path)
for local_name in local_name_list:
src = os.path.join(local_path, local_name)
if os.path.isdir(src):
try:
self.ftp.mkd(local_name)
except Exception as err:
self.debug_print("Directory already exists %s ,specific error description: %s" % (local_name, err))
self.debug_print("upload_file_tree()---> Uploading directory: %s" % local_name)
self.upload_file_tree(src, local_name)
else:
self.debug_print("upload_file_tree()---> Uploading file: %s" % local_name)
self.upload_file(src, local_name)
self.ftp.cwd("..")
def close(self):
""" Exit FTP
"""
self.debug_print("close()---> Exiting FTP")
self.ftp.quit()
self.log_file.close()
def debug_print(self, s):
""" Print log
"""
self.write_log(s)
def deal_error(self, e):
""" Handle error exceptions
Parameters:
e: Exception
"""
log_str = 'An error occurred: %s' % e
self.write_log(log_str)
sys.exit()
def write_log(self, log_str):
""" Record log
Parameters:
log_str: Log
"""
time_now = time.localtime()
date_now = time.strftime('%Y-%m-%d', time_now)
format_log_str = "%s ---> %s \n " % (date_now, log_str)
print(format_log_str)
self.log_file.write(format_log_str)
def get_file_list(self, line):
""" Get file list
Parameters:
line:
"""
file_arr = self.get_file_name(line)
# Remove . and ..
if file_arr[1] not in ['.', '..']:
self.file_list.append(file_arr)
def get_file_name(self, line):
""" Get file name
Parameters:
line:
"""
pos = line.rfind(':')
while (line[pos] != ' '):
pos += 1
while (line[pos] == ' '):
pos += 1
file_arr = [line[0], line[pos:]]
return file_arr
if __name__ == "__main__":
# Clear log
path = os.path.dirname(os.path.realpath(__file__)) # Script path
if os.path.exists(path + '/log.txt'):
log_file = path + '/log.txt 'if os.sep == "/" else path + '\\' + 'log.txt'
subprocess.Popen(f'rm -rf {log_file}', shell=True)
time.sleep(1)
my_ftp = MyFTP("xxx.ftp.upyun.com")
my_ftp.login("xxx/xxx", "xxx")
# Download a single file
# my_ftp.download_file("E:/code_zone/image_bed/image/wallpaper/1.jpg", "/image/wallpaper/1.jpg")
# Upload a single file
# my_ftp.upload_file("G:/ftp_test/Release/XTCLauncher.apk", "/App/AutoUpload/ouyangpeng/I12/Release/XTCLauncher.apk")
# Download directory
# image.cpen.top/image/ → Local E:/code_zone/image_bed/image/ (Local image hosting directory, Youpai Cloud path)
if os.sep == "\\":
my_ftp.download_file_tree("E:/code_zone/image_bed/image/", "/image/")
elif os.sep == "/": # aliyun
my_ftp.download_file_tree("/root/code_zone/image_bed/image/", "/image/")
# Upload directory
# Local E:/code_zone/image_bed/image/ → image.cpen.top/image/ (Local image hosting directory, Youpai Cloud path)
if os.sep == "\\": # Windows
my_ftp.upload_file_tree("E:/code_zone/image_bed/image/", "/image/")
my_ftp.close()
elif os.sep == "/": # aliyun
my_ftp.upload_file_tree("/root/code_zone/image_bed/image/", "/image/")
my_ftp.close()
# Command
# python E:/code_zone/tools/python-ftp/ftp.py
# python3 /root/code_zone/tools/python-ftp/ftp.py
2.2.2 Explanation
my_ftp.login("Username xxx/xxx", "Password xxx")
Reference https://techs.upyun.com/videos/cdnpage/creating_storage.html
Later, the script will be uploaded to the cloud server, and through a scheduled task, it will sync every 15 minutes to keep GitHub and Youpai Cloud image hosting consistent.
# root @ CentOS in ~ [18:05:59]
$ crontab -l
*/15 * * * * cd /root/code_zone/image_bed/; git pull; python3 /root/code_zone/tools/python-ftp/ftp.py; bash git.sh
After migrating the image hosting, when calling image resources in the blog, the browser automatically jumps from http to https, causing the images to fail due to the lack of a certificate, so I applied for an SSL certificate and uploaded it to Youpai Cloud.
2.3 Write a script to batch modify article content#
Source reference https://blog.csdn.net/qq_38150250/article/details/118026219
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# Source reference https://blog.csdn.net/qq_38150250/article/details/118026219
import os
import re
# File search find . -name file_name -type f
# Search function: search_path search root path
# Get article path
def search(search_path, search_result):
# Get all files in the current path
all_file = os.listdir(search_path)
# For each file
for each_file in all_file:
# If the file is a directory
if os.path.isdir(search_path + each_file):
# Recursively search
search(search_path + each_file + '/', search_result)
# If it is a file to be searched
else:
if re.findall('.*\.md$', each_file) == [each_file]:
# Output path
search_result.append(search_path + each_file)
# Replace sed -i 's/old_str/new_str/'
# Text replacement replace_file_name file path to be replaced, replace_old_str character to be replaced, replace_new_str new character
def replace(replace_file_name, replace_old_str, replace_new_str):
with open(replace_file_name, "r", encoding = "UTF-8") as f1:
content = f1.read()
f1.close()
t = content.replace(replace_old_str, replace_new_str)
with open(replace_file_name, "w", encoding = "UTF-8") as f2:
f2.write(t)
f2.close()
# Places that need to be changed
#path = 'E:/code_zone/.history/20220831_blog/source/_posts/'
path_list = [
'E:/code_zone/hexo-source/source/_posts/',
'E:/code_zone/hexo-source-butterfly/source/_posts/',
'E:/code_zone/hexo-source-diary/source/_posts/',
]
old_str = 'https://image.cpen.top/image/'
new_str = 'https://image.cpen.top/image/'
search_result = []
if __name__ == '__main__':
result = [] # Store file paths
# Default current directory
# path = os.getcwd()
for path in path_list:
search(path, result) # Get article paths
count = 0
for file_name in result:
replace(file_name, old_str, new_str)
count += 1
print("{} done {}".format(file_name, count))
# Command
# python E:/code_zone/tools/python-replace/replace.py
2.3.1 Explanation
search
function specifies the file type as .md, which can obtain the complete path of the article;
path_list
list stores the parent directory paths of the articles to be modified, which can recursively query subdirectories;
old_str
content to be replaced
new_str
new content