透過Restful API傳送檔案

通常如果我們有傳送檔案的需求時,在http的世界裡,我們可以透過restful api定義的兩種content-type來實作:

  • application/x-www-form-urlencoded
  • multipart/form-data

雖然說,之前在工作中也有過一些使用上的經驗,但趁這次工作上又需要碰到,所以來做點筆記。

application/x-www-form-urlencoded

The application/x-www-form-urlencoded content type describes form data that is sent in a single block in the HTTP message body. Unlike the query part of the URL in a GET request, the length of the data is unrestricted.

這裡可以看到,當如果我們需要透過http 傳送單一檔案時,我們可以在body中放入我們需要傳送的檔案;而使用這種方式傳送檔案時,其內容必須遵循以下的encoding rules:

* Control names and values are escaped. Space characters are replaced by `+’, and then reserved characters are escaped as described in [RFC1738], section 2.2: Non-alphanumeric characters are replaced by `%HH’, a percent sign and two hexadecimal digits representing the ASCII code of the character. Line breaks are represented as “CR LF” pairs (i.e., `%0D%0A’).

* The control names/values are listed in the order they appear in the document. The name is separated from the value by `=’ and name/value pairs are separated from each other by `&’.

stackoverflow的這篇文章來看,一般使用這種方式傳送檔案時,平均會增加至少33%的額外流量,所以並不是一個很有效率的方法。

multipart/form-data

In the multipart/form-data content type, the HTTP message body is divided into parts, each containing a discrete section of data.

Each message part requires a header containing information about the data in the part. Each part can contain a different content type; for example, text/plainimage/pngimage/gif, or multipart/mixed. If a parameter specifies multiple files, you must specify the multipart/mixed content type in the part header.

這種類型的content-type,定義了client可以在message body中傳送多個不同的檔案,此外,我們必須為每種檔案設定其檔案類型。

在encoding的部分,這裡被沒有強制使用base64,而是讓客戶端可以指定conding的方式。

根據w3c的文件中提到,multipart 是較適合用來傳送binary file或一些non-ASCII的檔案。

application/octet-stream

通常這種類型的content-type是用來給瀏覽器下載檔案時使用,瀏覽器收到這類型的content-type時,預設就會直接下載到本地端。

但在stackoverflow的這篇文章中有提到,我們其實可以直接透過POST 直接設定content-type 為application/octet-stream,並且在加上以下的header 設定就可以直接指定要傳送的檔案。

Connection: close
Content-Type: application/octet-stream
Content-Length: <content-size>

結論

  • multipart/form-data 已發展多年了,且從文件上來看,在最初定義時就是為了傳送二進制的檔案類型,所以我們若有這方面的需求時,應該盡量使用這個content-type。
  • 如果我們的需求只是傳送單一的小檔案,且又可以使用base64 encoding時,使用application/x-www-form-urlencoded也不失為另一個可考慮的選項。

Reference

WordPress 搬家

忙了一個下午,終於把Wordpress從原本的RespberryPi上搬到目前的Ubuntu開發主機上! 而會花那麼多時間,主要是因為在開發機上,我想要用docker-compose的方式去佈署這個Wordpress,而不是直接安裝Wordpress於主機上。

這邊簡單列一下整個流程與相關的細節

佈署Wrodpress docker-compose

主要可以參考wrodpress 官方提供的docker-compose,就可以在有docker環境下的機器順利執行整套wordpress。

以下是我更改後的wordpress docker-compose.yaml

version: "3.3"
 services:
   db:
     image: mariadb:10.5
     ports:
       - "3306:3306"
     volumes:
       - "./my-docker-volumes/db:/var/lib/mysql"
     environment:
       MYSQL_ROOT_PASSWORD: mariadb-root-password
       MYSQL_DATABASE: wordpress-user
       MYSQL_USER: wordpress-user
       MYSQL_PASSWORD: wordpress-password
     restart: always
  wordpress:
     depends_on:
       - db
     image: wordpress:5.6
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress-user
       WORDPRESS_DB_PASSWORD: wordpress-password
     volumes:
       - "./my-docker-volumes/wordpress:/var/www/html"

這邊所使用的MariaDB與Wordpress版本都是以當初在RespberryPi上的版本為主,主要是希望從RespberryPi回復資料時,不會造成一些資料上的問題。

設定本機的Nginx

這邊一開始設定時,也遇到了些css上的問題,後來參考了這連結以後也順利的解決了!
(原本以為是wp-config.php內部有檔參數要改,所以也花了不少時間在這上面)

設定完成的nginx 設定檔如下:

server {
     listen 80;
     server_name blog.gechen.xyz;
     rewrite ^(.*) https://$host$1 permanent;
 }
 server {
    listen 443 ssl;
    server_name blog.gechen.xyz;
    index index.php;
    ssl_certificate /cert_path/fullchain.pem;
    ssl_certificate_key /cert_path/privkey.pem;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1.1 TLSv1.2;
    client_max_body_size 100M;
    location / {
        include snippets/wp-reverse-proxy.conf;
        proxy_pass http://localhost:8000/;
    }
    location /login {
        try_files $uri $uri/ /wp-login.php; 
    }
 }

wp-reverse-proxy.conf的設定檔如下:

add_header X-Frame-Options SAMEORIGIN;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_hide_header X-Frame-Options;

設定Let’s encrypt certificate

之前的certbot也是放在RespberryPi上,所以也趁這個機會搬到目前的開發主機上,所以直接參考了之前的設定方式,在目前的主機上完成了wildcard certificate的申請。

備份與還原Wordpress database

這個部分,我是直接透過mysqpdump的方式,直接把先前整個wordpress db用下面的指令dump:

mysqldump -u wordpress-user -p --all-databases > backup.sql;

再直接還原到新的wordpress db 上:

mysql -h new-wordpress-ip-address -u wordpress-user -p wordpress-db < backup.sql

備份與還原Wordpress files

這部分目前還是有些問題,我主要是參考這篇文章提到的備份整個wordpress files,但在把備份資料夾從Respberry Pi 搬到開發主機並直接用docker-compose mount到內部資料夾後,佈景主題與外掛都有出了點問題!

看來這部分還需要花點時間了解,要如何完整的備份一個Wordpress 網站 !!!

References

為WordPress 升級 https

申請Letsencrypt 憑證

由於這邊是使用ubuntu 20.04,所以會直接使用下面的指令來安裝certbot

sudo apt install certbot python3-certbot-nginx

接下來,再使用certbot來申請由Letsencrypt發放的憑證。

下面的指令,我們透過certbot 申請了一組wildcard certificate,其中為了證明這個網域是我們所有的,我們必須完成一組ACME挑戰!

sudo certbot certonly --preferred-challenges dns --manual  -d *.gechen.xyz --server https://acme-v02.api.letsencrypt.org/directory

在完執行完上述的指令以後,terminal上應該會出現一串字串,而且我們要做就是將那字串新增至DNS TXT Record,並在設定TXT Record設定完成以後,繼續certbot的申請流程。

一切順利的話,certbot 會將申請到的憑證安裝至指定的路徑下,並且有三個月的效期,三個月後我們還得再重新申請效期的延長

/etc/letsencrypt/live/gechen.xyz/

升級Wordpress以使用https

接下來要做的更改Nginx的設定檔以使用我們新申請的憑證

server {
     listen 80;
     listen [::]:80;
     server_name blog.gechen.xyz;
     rewrite ^(.*) https://$host$1 permanent;
 }

 server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name blog.gechen.xyz;
    index  index.php; 
    root /var/www/html/wordpress; 
    ssl_certificate /etc/letsencrypt/live/gechen.xyz/fullchain.pem;  
    ssl_certificate_key /etc/letsencrypt/live/gechen.xyz/privkey.pem; 
    ssl_session_timeout 5m; 
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 
    # ssl_trusted_certificate /etc/letsencrypt/live/gechen.xyz/fullchain.pem;
   client_max_body_size 100M; 
    error_log /var/log/nginx/secure_ssl_error.log; 
    access_log /var/log/nginx/secure_ssl_access.log; 
    location / {
      try_files $uri $uri/ /index.php?$args; 
    } 
    location ~ \.php$ {     
        include snippets/fastcgi-php.conf;     
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;     
        fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;     
        include fastcgi_params;
  }
 }

更新Wordpress的網址

這邊可以直接參考之前設定Wordpress的相關步驟來設定新的https的網址

到這邊我們就升級完成了

在Respberry Pi Ubuntu上安裝wordpress

記錄一下整個安裝的過程

安裝php與相關的套件

sudo add-apt-repository ppa:deadsnakes/ppa 
sudo apt-get update
sudo apt -y install php7.4 php7.4-fpm php-mysql

安裝 Nginx

sudo apt-get install nginx

安裝 MariaDB與設定 MariaDB

sudo apt-get install mariadb-server
sudo mysql_secure_installation

mysql -u root -h localhost -p

SET PASSWORD FOR 'root'@'localhost' = PASSWORD('MY_NEW_PASSWORD');


CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'newpassword';

CREATE DATABASE wordpress;
GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'localhost'
FLUSH PRIVILEGES;

安裝wordpress

wget -c http://wordpress.org/latest.tar.gz
tar -xzvf latest.tar.gz
sudo cp -R wordpress/ /var/www/html/wordpress
sudo chmod -R 775 /var/www/html/wordpress
sudo mv wp-config-sample.php wp-config.php

設定Wordpress要連接的資料庫

編輯wp-config.php 檔案以後再更新下面的資訊

// ** MySQL settings - You can get this info from your web host ** //
 /** The name of the database for WordPress */
 define( 'DB_NAME', 'the_db_name' );
 /** MySQL database username */
 define( 'DB_USER', 'the_db_user' );
 /** MySQL database password */
 define( 'DB_PASSWORD', 'db_password );
 /** MySQL hostname */
 define( 'DB_HOST', 'localhost' );

設定Nginx

在安裝好nginx以後,可以先到/etc/nginx/sites-enabled/中將預設所有目前服務的http 網站移除,然後將wordpress的設定檔 wordpress.conf 加入到/etc/nginx/conf.d/ 這個資料夾下

server {
    listen 80;
    server_name blog.gechen.xyz; 
    index  index.php; 
    root /var/www/html/wordpress; 
    client_max_body_size 100M; 
    error_log /var/log/nginx/wordpress_error.log; 
    access_log /var/log/nginx/wordpress_access.log; 
    location / {     
        try_files $uri $uri/ /index.php?$args; 
    } 
    location ~ \.php$ {     
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;     
        fastcgi_param   
        SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
 }

關閉Apache

/etc/init.d/apache2 stop

WordPress 網址

最後在設定中,還需要設定目前網站的位扯;而這個部分可以從wordpress 的管理界面中設定,或者是透過直接設定對應的DB 資料。

如果是透過DB 設定的話,則可以透過以下的SQL 來更新:

update wp_options set option_value = "http://blog.gechen.xyz" where option_name = "home" OR option_name = "siteurl";