2026年7月30日 星期四

在 Ubuntu 26.04 架設網域名稱伺服器(DNS)及相關設定說明

        BIND(Berkeley Internet Name Domain 伯克利網際網路域名) 是全世界最廣泛用來提供DNS服務的軟體,能夠將 網域名稱 <-對應-> IP 。提供兩種工具:1.named  2.rndc (遠端管理named 工具)
        1.安裝BIND
           sudo apt install bind9

        2.檢測是否安裝成功
            sudo ss -tulnp | grep named
            各個指令的意義:
             ss 顯示 Socket 相關的資訊
             -t 顯示 TCP連線
             -u 顯示 UDP連線
             -l 顯示 監聽中的連線
             -n 顯示 連接埠數字
             -p 顯示 占用連線的行程(process)
            (1).當 TCP 與 UDP 監聽 連接埠 53 表示 BIND 安裝成功
            (2).當TCP 監聽 連接埠 953 表示 rndc 會用到的連接埠  
       一、named:BIND 提供的守護程式(daemon)
       1.主要設定檔- named.conf:預設位置 /etc/bind/
        named.conf 內容:
       (1) named.conf.options 用來存放 options (選項),內容:
    其中 directory "/var/cache/bind";
                    directory 指定一個目錄,作為DNS資料庫,預設目錄為 /var/cache/bind
            forwarders {    
                 0.0.0.0;       
            };                     
                    forwarders 指定多個IP,以分號 ; 區隔。當我們DNS 無法回應使用者的查詢請求時,
                    轉移(forward) 指定的IP (就目前的IP為 0.0.0.0)
            dnssec-validation auto;
                    讓我們DNS 支援 DNSSEC (Domain Name System Security Extension)
            Listen-on-v6 設定監聽 IPv6範圍
       (2) named.conf.local 用來存放本地和使用者自定的 Zone 區塊。設定A.正解 B.反解
            A.正解(用網域名稱 查 IP) 
            B.反解(用IP 查 網域名稱)
       (3) zones.rfc1918 用來定義私有網域為 10.0.0.0 ~10.255.255.255、172.16.0.0~172.31.255.255、192.168.0.0 ~ 192.168.255.255,內容為
       172.16.0.0~172.16.255.255 zone的反解寫法:
        zone "16.172.in-addr.arpa" {    ...    };
        那 zone "16.172.in-addr.arpa" { type master; file "/etc/bind/db.empty";  };
       file 指定一個檔案給 zone 區塊對應的網域或IP,
             而該檔案內有DNS資源紀錄(Resource Record  RR)。        
       /etc/bind/db.empty 是 BIND 內建的設定檔,內容如下:
       type 設定值有三:A.master B.slave C.hint
        A.master 表示 zone 是主從架構的DNS中的 主要主機 
        B.slave   表示 zone 是主從架構的DNS中的 從屬主機
        C.hint     用於對應 . 的時候。 . 表示 [根域名](root name)
       (4) db.empty 內容:
       $TTL 86400
   
    $TTL 設定此RR(資源紀錄Resource Record) 作為解答的快取時間,單位是秒。
       $TTL 86400 意思是使用者的查詢請求只要問過DNS一次,就可以持續一整天(86400秒)

        @   IN  SOA localhost. root.localhost. (                                                   
                           1     ; Serial      
                 604800     ; Refresh     
                   86400     ; Retry       
               2419200     ; Expire      
                   86400 )   ; Negative Cache TTL
                   @                                       代表   zone name :目前網域(Zone Root )
                  IN                                       代表   Class          :InterNet 類別
  SOA(Start Of Authority 授權開始)  代表   這筆DNS紀錄值的格式
              localhost.                               代表   主要(master)主機的網域名稱                  
           root.localhost.                           代表   管理者的電子信箱
              Serial                                      代表   版本序號
              Refresh                                   代表   從屬(slave)主機的更新時間
               Retry                                     代表    重試的時間間隔
             Expire                                     代表    逾時時間
      Negative Cache TTL                     代表    最小的TTL值

        @   IN   NS   localhost.
                NS(Name Server)                代表    指定此網域的權威 DNS 伺服器                                      
      其他常用的DNS 資源記錄類型,如下:
      A            :指定網域名稱所對應的IPv4。正解用。
      AAAA   :指定網域名稱所對應的IPv6。正解用。
      PTR       :指定IP所對應的網域名稱。反解用。
      CNAME:指定別名。正解用。
      MX        :用於指定電子郵件交換器的主機名稱。
       TXT      :指定網域名稱對應某筆字串資料,通常用於驗證網域所有權。

資料來源:

2026年6月29日 星期一

Windows 11 建立本地端 AI (Ollama + qwen2.5-coder:7B + docker + webui)

0.自己本地端設備環境:Ultra 7 155H + 32G RAM + Intel Arc Pro Graphics 128M 

1.ollama 下載 ->下載後點兩下進行安裝

或是 以系統管理員身分開啟 PowerShell 進行安裝
安裝指令:irm https://ollama.com/install.ps1 | iex
  (1).測試是否安裝成功:ollama --version
  (2).檢查有多少模型:ollama list   (3).下載模型指令:ollama pull qwen2.5-coder:7B (4).安裝模型指令:ollama run qwen2.5-coder:7B

2. docker 安裝前,要先安裝 WSL
    (1). 以系統管理員身分開啟 PowerShell 
        安裝指令:wsl --install
    (2). 重啟電腦
    (3). 確認是否安裝成功指令:wsl --version

3.下載 docker desktop ->下載後點兩下進行安裝

或是 以系統管理員身分開啟 PowerShell 進行安裝-> 重啟電腦
安裝指令:winget install -e --id Docker.DockerDesktop
確認是否安裝成功指令:docker --version

4.下 powershell 指令啟動 Open WebUI:
docker run -d ^
--name open-webui ^
-p 3000:8080 ^
-e OLLAMA_BASE_URL=http://host.docker.internal:11434 ^
-v open-webui:/app/backend/data ^
--restart unless-stopped ^
ghcr.io/open-webui/open-webui:main

安裝完後,重開機。該如何啟動本地端AI?
    (1)由於重開機後,ollama 會開機後啟動。所以無需另行啟動。
    (2)只要啟動 docker desktop 即可。
    (3)打開瀏覽器 http://localhost:3000

2026年6月25日 星期四

彰化縣學校資通安全稽核計畫網站相關備忘事項

彰化縣學校資通安全稽核網站:https://audit.mychc.site/
彰化縣學校資通安全稽核網站管理登入:https://audit.mychc.site/admin/
登入之後的設定:
1.稽核設定->年度稽核學校->新增學校

完成之後,就可以有四個選項可以選擇,其中比較重要的是
1.指派稽核員:

2.訪客稽核員邀請:

其中
1.指派稽核員->選擇稽核員(現有帳號)->新增
2.訪客稽核員邀請->掃描QRcode->邀請訪客稽核員->立即開始

值得注意的是1.指派稽核員完成之後,才能開始與建立稽核報告

建立稽核報告完成之後,只能選擇儲存。定稿之後,才能使用QRcode簽核
就可以選擇稽核身份進行簽核

實際經驗分享:
1.稽核員 與受稽核學校人員 都會加入 一個群組。所以可以將相關連結 貼到群組即可。

只要點兩下,就能將input資料夾內所有PDF進行容量壓縮-版本02

系列文章:
01.python 不管何時何地,只要點兩下,資料夾內的所有pdf都會合併成一個pdf
https://skjhcreator.blogspot.com/2022/06/pythonpdfpdf.html
02.python 只要點兩下,分別對各資料夾內的pdf合併,合併後的檔名要讓人知道來自哪個資料夾 
https://skjhcreator.blogspot.com/2022/06/python-pdf.html
03. 只要點兩下,就能將一堆的Doc與Docx 轉成 PDF
03.https://skjhcreator.blogspot.com/2023/05/docdocx-pdf.html
04.只要點兩下,就能將一堆的JPG轉成一個PDF,並以JPG所在的資料夾名稱為PDF的檔名
04.https://skjhcreator.blogspot.com/2023/06/jpgpdfjpgpdf.html
05.只要點兩下,就能將放進input的一堆PDF轉成各自的WORD
05.https://skjhcreator.blogspot.com/2023/10/inputpdfword.html
06.只要點兩下,就能將放進input的一堆PDF轉成在ouput資料夾內的各自的WORD 
06.https://skjhcreator.blogspot.com/2023/10/inputpdfouputword.html
07.只要點兩下,就能夠將InputAndOutput資料夾底下的子子孫孫資料夾內所有Word通通轉成PDF
07.https://skjhcreator.blogspot.com/2024/09/word-word-pdf.html
08.只要點兩下,就可以將資料夾input內的所有Word通通轉成一個PDF
08.https://skjhcreator.blogspot.com/2025/07/inputwordpdf.html
09.只要點兩下兩次,就能依照設定拆分指定PDF頁數並合併成設定好的一個PDF
09.https://skjhcreator.blogspot.com/2025/09/pdfpdf.html
10.只要點兩下,就能將input資料夾內所有PDF進行容量壓縮-版本01
10.https://skjhcreator.blogspot.com/2026/04/inputpdf.html
11.只要點兩下,就能將input資料夾內所有PDF進行容量壓縮-版本02
11.https://skjhcreator.blogspot.com/2026/06/inputpdf-02.html

Just double-click to compress the file size of all PDFs in the input folder – Version 02.
        PDF 壓縮設定為:
screen = 最小檔案,適合螢幕閱讀
ebook = 一般文件,推薦使用
printer = 列印品質
prepress = 印刷品質
        由於版本01 將壓縮設定 固定在 ebook,也就是一般文件,推薦使用。當想要提高壓縮比,版本01就無法滿足。所以版本02就將壓縮設定寫進 setting.ini,若 setting.ini 不見了,版本02還可以生成。讓使用者可以修改 setting.ini,來滿足其需求。

PDF Compression Settings:

  • screen = Smallest file size, suitable for on-screen reading
  • ebook = Standard document quality, recommended for most uses
  • printer = Printer-quality output
  • prepress = Prepress quality for professional printing

In Version 01, the compression setting was fixed to ebook (standard document quality), which is recommended for general use. However, when a higher compression ratio is required, Version 01 cannot meet that need.

To address this limitation, Version 02 stores the compression setting in setting.ini. Users can modify the setting in setting.ini to suit their specific requirements. In addition, if setting.ini is missing, Version 02 can automatically regenerate it, ensuring the program continues to function properly.


下載檔案解壓密碼:demo1234
Here is the website where you can download the program and find instructions:
Download。Extraction Password: demo1234
使用教學(Instructional videos):


方法:Python + Ghostscript
一、下載與安裝 Ghostscript
https://www.ghostscript.com/download/gsdnld.html 下載後,點兩下進行安裝

二、Python 程式碼
檔案:pyPdfCompress01.py
檔案內容:

import os
import subprocess
import shutil
import webbrowser
import sys
import argparse
import platform
import configparser

INPUT_DIR = "input"
OUTPUT_DIR = "output"
SETTINGS_FILE = "settings.ini"

QUALITY_SETTINGS = {
    "screen": "/screen",
    "ebook": "/ebook",
    "printer": "/printer",
    "prepress": "/prepress"
}


def create_default_settings():
    config_content =
"""
; ==========================================
; PDF 壓縮設定
; ==========================================
;
; 可用品質:
;
; screen   = 最小檔案,適合螢幕閱讀
; ebook    = 一般文件,推薦使用
; printer  = 列印品質
; prepress = 印刷品質
;
; 修改 quality 的值即可
;
; ==========================================

[default]
quality = ebook

; quality = screen
; quality = printer
; quality = prepress
"""

    with open(SETTINGS_FILE, "w", encoding="utf-8") as f:
        f.write(config_content)


def load_settings():
    if not os.path.exists(SETTINGS_FILE):
        create_default_settings()
        print(f"📄 已建立預設設定檔: {SETTINGS_FILE}")

    config = configparser.ConfigParser()
    config.read(SETTINGS_FILE, encoding="utf-8")

    quality = config.get(
        "default",
        "quality",
        fallback="ebook"
    ).lower()

    if quality not in QUALITY_SETTINGS:
        print(
            f"⚠ settings.ini 中的 quality='{quality}' 無效"
        )
        print("⚠ 已自動改用 ebook")
        quality = "ebook"

    return quality


def check_ghostscript():
    gs = shutil.which("gswin64c") or shutil.which("gs")

    if gs is None:
        print("❌ [未安裝 Ghostscript]")

        if platform.system() == "Windows":
            print("👉 請下載 Windows 版本 (gswin64)")
        else:
            print("👉 請下載對應系統版本")

        print("👉 即將開啟下載頁面...")

        webbrowser.open(
            "https://www.ghostscript.com/download/gsdnld.html"
        )

        print("⚠ 若已安裝,請確認 Ghostscript 已加入 PATH")

        sys.exit(1)

    return gs


def compress_pdf(
    gs_command,
    input_path,
    output_path,
    quality
):
    cmd = [
        gs_command,
        "-sDEVICE=pdfwrite",
        "-dCompatibilityLevel=1.4",
        f"-dPDFSETTINGS={QUALITY_SETTINGS[quality]}",
        "-dNOPAUSE",
        "-dQUIET",
        "-dBATCH",
        f"-sOutputFile={output_path}",
        input_path
    ]

    result = subprocess.run(
        cmd,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.PIPE,
        text=True
    )

    if result.returncode != 0:
        raise RuntimeError(result.stderr)


def process_file(
    gs_command,
    file,
    quality
):
    input_path = os.path.join(INPUT_DIR, file)

    name, ext = os.path.splitext(file)

    output_path = os.path.join(
        OUTPUT_DIR,
        f"{name}_compressed{ext}"
    )

    compress_pdf(
        gs_command,
        input_path,
        output_path,
        quality
    )

    original_size = os.path.getsize(input_path)
    compressed_size = os.path.getsize(output_path)

    print(f"✔ {file}")
    print(f"   原始: {original_size / 1024:.2f} KB")
    print(f"   壓縮: {compressed_size / 1024:.2f} KB")

    if compressed_size >= original_size:
        print("   ⚠ 壓縮後反而變大,已刪除壓縮檔\n")
        os.remove(output_path)
        return original_size, original_size

    reduction = original_size - compressed_size
    ratio = reduction / original_size * 100

    print(f"   減少: {reduction / 1024:.2f} KB")
    print(f"   壓縮率: {ratio:.2f}%\n")

    return original_size, compressed_size


def main():
    default_quality = load_settings()

    parser = argparse.ArgumentParser(
        description="PDF 批次壓縮工具"
    )

    parser.add_argument(
        "--quality",
        choices=QUALITY_SETTINGS.keys(),
        default=default_quality,
        help=f"壓縮品質 (預設: {default_quality})"
    )

    args = parser.parse_args()

    print(f"🔧 壓縮模式: {args.quality}")
    print()

    gs_command = check_ghostscript()

    if not os.path.exists(INPUT_DIR):
        os.makedirs(INPUT_DIR)

        print(
            f"📁 已建立資料夾: {INPUT_DIR}"
        )
        print("請放入 PDF 後重新執行")

        return

    if not os.path.exists(OUTPUT_DIR):
        os.makedirs(OUTPUT_DIR)

    files = [
        f for f in os.listdir(INPUT_DIR)
        if f.lower().endswith(".pdf")
    ]

    if not files:
        print("📂 input 資料夾內沒有 PDF")
        return

    total_original = 0
    total_compressed = 0

    for file in files:
        try:
            original, compressed = process_file(
                gs_command,
                file,
                args.quality
            )

            total_original += original
            total_compressed += compressed

        except Exception as e:
            print(f"✖ 失敗: {file}")
            print(e)
            print()

    print("================================")

    if total_original > 0:
        reduction = total_original - total_compressed
        ratio = reduction / total_original * 100

        print(
            f"原始總大小: "
            f"{total_original / 1024 / 1024:.2f} MB"
        )

        print(
            f"壓縮總大小: "
            f"{total_compressed / 1024 / 1024:.2f} MB"
        )

        print(
            f"節省空間: "
            f"{reduction / 1024 / 1024:.2f} MB"
        )

        print(
            f"總壓縮率: "
            f"{ratio:.2f}%"
        )

    print("================================")
    os.system("PAUSE")


if __name__ == "__main__":
    main()

















2026年5月19日 星期二

只要點兩下,就能將input資料夾內所有m4a 轉檔為 mp3

         最近需要將m4a檔案轉檔為mp3,所以寫python程式來處理。希望將很多的m4a放進input資料夾內,只要點兩下滑鼠就能將這些m4a 通通轉檔為 mp3。
         Recently, I needed to convert M4A files into MP3 format, so I wrote a Python program to handle it. The idea is to place multiple M4A files into an input folder, then simply double-click the program to automatically convert all the M4A files into MP3 files.

下載檔案解壓密碼:demo1234
Here is the website where you can download the program and find instructions:
Download。Extraction Password: demo1234
使用教學(Instructional videos):


以下是開發過程與原始碼 (Development process and code):
01.安裝 python 套件(Install Python packages.)
pip install pydub

02.下載 ffmpeg 並加入 PATH (Download FFmpeg and add it to the system PATH.)
程式名稱(Program name):install_ffmpeg.bat
程式內容(Code):
@echo off
:: 檢查是否為系統管理員
net session >nul 2>&1
if %errorLevel% neq 0 (
    echo 正在要求系統管理員權限...
    powershell -Command "Start-Process '%~f0' -Verb RunAs"
    exit /b
)

setlocal

echo.
echo ===== 安裝 FFmpeg =====
echo.

REM 建立 ffmpeg 資料夾
if not exist C:\ffmpeg (
    mkdir C:\ffmpeg
)

REM 下載 ffmpeg
echo 下載 FFmpeg...
powershell -Command ^
"Invoke-WebRequest -Uri https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip -OutFile C:\ffmpeg\ffmpeg.zip"

REM 解壓縮
echo 解壓縮中...
powershell -Command ^
"Expand-Archive -Path C:\ffmpeg\ffmpeg.zip -DestinationPath C:\ffmpeg -Force"

REM 找到 bin 路徑
for /d %%i in (C:\ffmpeg\ffmpeg-*) do (
    set "FFMPEG_BIN=%%i\bin"
)

REM 加入系統 PATH
echo 加入 PATH...
setx PATH "%PATH%;%FFMPEG_BIN%" /M

echo.
echo ===== FFmpeg 安裝完成 =====
echo.
echo 請重新開啟 CMD 或 PowerShell
echo 測試指令:
echo ffmpeg -version
echo.

pause

03.python 程式名稱(Python script filename.):pyM4a2Mp3.py
程式內容(Code):
from pydub import AudioSegment
import os

input_folder = "input"
output_folder = "output"

# 如果 output_folder 不存在就建立
if not os.path.exists(output_folder):
    os.makedirs(output_folder)

for file in os.listdir(input_folder):
    if file.endswith(".m4a"):
        m4a_path = os.path.join(input_folder, file)

        mp3_filename = os.path.splitext(file)[0] + ".mp3"
        mp3_path = os.path.join(output_folder, mp3_filename)

        # 讀取 m4a
        audio = AudioSegment.from_file(m4a_path, format="m4a")

        # 輸出 mp3
        audio.export(mp3_path, format="mp3")

        print(f"已轉換: {file} -> {mp3_filename}")

print("全部轉換完成")

2026年5月16日 星期六

只要點兩下,就能依據文字檔內容,創建對應的資料夾

         最近針對書本的目錄,需要創建相對應的資料夾。過去要找到該書的目錄網頁,一行一行地複製網頁文字,再一行一行地創建資料夾、重新命名、貼上。或是直接依據該書的紙本目錄,一行一行地看,創建資料夾、重新命名、一行一行地打字。
        Recently, I often need to create corresponding folders based on a book’s table of contents. In the past, I had to find the webpage containing the book’s contents, copy the text line by line, and then create folders one by one, rename them, and paste the titles manually. Another method was to look at the printed table of contents and type each line manually while creating and renaming folders one at a time.
         總覺得速度很慢,希望能夠將目錄網頁內容複製(或打入)到一個文字檔,程式依據該文字檔內容,一行一行地創建對應的資料夾。
         I always felt this process was too slow. I hope to be able to copy the contents from a webpage (or type them in) into a text file, and then have a program automatically create the corresponding folders line by line according to the contents of that text file.


下載檔案解壓密碼:demo1234
Here is the website where you can download the program and find instructions:
Download。Extraction Password: demo1234
使用教學(Instructional videos):








以下是開發過程與原始碼 (Development process and code):
程式名稱(Program name):pyTxt2Folder.py
程式內容(Code):
import os

# 開啟 input.txt,逐行讀取內容
for folder_name in open("InputFileName.txt", encoding="utf-8"):

    # 移除前後空白與換行符號
    folder_name = folder_name.strip()

    # 如果該行不是空白
    if folder_name:

        # 建立資料夾
        # exist_ok=True 表示資料夾已存在時不會報錯
        os.makedirs(folder_name, exist_ok=True)

        # 顯示建立成功訊息
        print(f"已建立資料夾:{folder_name}")

在 Ubuntu 26.04 架設網域名稱伺服器(DNS)及相關設定說明

        BIND(Berkeley Internet Name Domain 伯克利網際網路域名) 是全世界最廣泛用來提供DNS服務的軟體,能夠將 網域名稱 <-對應-> IP 。提供兩種工具:1.named  2.rndc (遠端管理named 工具)   ...