顯示具有 工作備忘 標籤的文章。 顯示所有文章
顯示具有 工作備忘 標籤的文章。 顯示所有文章
2023-02-21 10:33

[Python] Flask Log 配置

import os
import logging
import logging.handlers

from flask import Flask, g, request, json

app = Flask(__name__)


#[ Log 配置 ]#############################################################
 # 用來記錄無法處理的錯誤 (PS: 使用 WSGI 會依附 Apache 的設定,可以不用配置)

# https://docs.python.org/zh-cn/3/library/logging.html
formatter = logging.Formatter("%(asctime)s [%(levelname)s]  %(message)s")

# https://docs.python.org/zh-tw/3/library/logging.handlers.html#timedrotatingfilehandler
handler = logging.handlers.TimedRotatingFileHandler("log/web-api",
    when = "D",
    interval = 1,
    backupCount = 7,
    encoding = "UTF-8",
    delay = False,
    utc = True)
handler.setFormatter(formatter)

app.logger.addHandler(handler)
2023-02-21 10:13

[Python] Flask 自訂日期的 Json 轉換

import os
import datetime
import time

from flask import Flask, g, request, json
from flask.json import JSONEncoder


class CustomJsonEncoder(JSONEncoder):
    # 針對日期自訂 Json 轉換

    def default(self, obj):
        if isinstance(obj, datetime.date):
            return obj.isoformat().replace('T', ' ')

        return super().default(obj)


app = Flask(__name__)

app.json_encoder = CustomJsonEncoder
app.config['JSON_AS_ASCII'] = False  # 返回結果可以正確顯示中文
2023-02-21 10:05

[Python] Flask MySQL 連線管理

import os
import mysql.connector as sql

from werkzeug.exceptions import HTTPException, BadRequest
from flask import Flask, g, request, json


app = Flask(__name__)



#[ DB 處裡 ]#############################################################

# https://docsxyz.com/zh-hant/wiki/python/connector-python-connectargs
db_config = {
    'host'      : "localhost",
    'user'      : "XXXX",
    'passwd'    : "XXXX",
    'db'        : 'XXXX',
    'use_pure'  : True,
    'autocommit': True,
    'charset'   : 'utf8',
}


@app.before_request
def before_request():
    # 在 request 前開啟 DB 連線
    # g 是 Flask global 在每個 request 有獨立的 context

    g.cnt = sql.connect(**db_config)
    g.cursor = g.cnt.cursor(dictionary = True)



@app.after_request
def after_request(response):
    # 在 request 後結束 DB 連線

    cursor = g.get('cursor', None)
    if cursor is not None:
        # 當 cursor 還有 row 沒有取出,close 會發生錯誤
        if cursor.with_rows : cursor.fetchall()
        cursor.close()

    cnt = g.get('cnt', None)
    if cnt is not None:
        cnt.close()

    return response
2023-02-21 09:53

[Python] Flask 錯誤處裡


from werkzeug.exceptions import HTTPException, BadRequest
from flask import Flask, g, request, json

app = Flask(__name__)


#[ 錯誤處裡 ]#############################################################
@app.errorhandler(Exception)
def handle_exception(e):
    if isinstance(e, HTTPException): return e  # 讓 HTTPException 交由下一個處理

    app.logger.exception("Internal Server Error.")  # log 錯誤訊息
    if app.debug : return e

    return json.jsonify({'message': 'Internal Server Error.'}), 500


@app.errorhandler(HTTPException)
def handle_exception(e):
    response = e.get_response()
    return json.jsonify({'message': e.description}), e.code
2022-09-08 13:39

架構解釋

以前在專案開發時對分層架構所定下的原則,去避免不必要的地雷,以及每一層的要處裡的職權。

我會考慮分層架構,是希望提高系統的嚴謹度,以及 Method 的共用性,所以 Dao 層級就要避免功能導向,不然就是把單頁的程式拆分到多個層去而已。

 

DomainModel / ViewModel

  • 這三類都屬於 POCO 類型的物件,單純的資料載體,不允許有動作的 method,不可以外部取資料
  • 相同資料的欄位命名必須一致,反例: ProjectId, Pid, pid, PID, p_id
  • 有欄位就要有資料,明明有欄位定義卻不從 DB 取資料,這會造成不必要的雷

 

DomainModel

  • 欄位定義可與 DB 一致
  • 將 DB 的資料進行彙整,成為完整的資料體

 

ViewModel

欄位定義與 View 的表單(Form)一致,與 DomainModel 可能很相似,但有些欄位只會用在表單上,所以 DomainModel 不適合用在表單 Binding 上

例如:

  • 同意上述條款
  • 舊密碼, 新密碼, 確認密碼

 

Dao

  • 回傳 DomainModel
  • 定義單純的 BD 操作 List, GetById, Save, Insert, Update
  • 與 ORM 不同,是進一步將資料操作簡化
  • 保證 Method 的通用性,避免功能導向,例如: 有一個 Method 是專門為了A畫面功能而存在的

 

Service

  • 回傳 DomainModel
  • 驗證商業邏輯,如必要資料欄位,數值範圍等...
  • 調用一個或多個 Dao 來完成商業邏輯
  • 處理 DB 交易,來協調多個 Dao 的調用

 

Controller

  • 處理 DomainModel 到 ViewModel 的轉換
  • 調用 Service 進行 DomainModel 的資料處理
  • 調配 Responses 的結果 Html, Json, PDF ...
  • 驗證資料類型的正確,數值﹑日期
  • 負責 Access 的權限阻擋

 

View

  • View 是被動的,不能存取任何 Controller / Service / Dao
  • 負責資料呈現及格式化,如 日期﹑金額 ...
  • Ajax 只能對 Controller 調用

 

附註

  1. 同一層之間不可以互相參考,這容易發生循環參考,例如:
    • Controller 呼叫 Controller
    • Service 呼叫 Service
    • Dao 呼叫 Dao
  2. 同一層之間有相同邏輯可以抽離到 Support 類
     
  3. 建議定義命名規範,讓每一個人寫出來的程式像是同一個人寫的,好處是降低支援或接手的人的困難度
     
  4. 任何第三方的 API 都需要包裝,隔離直接相依的問題,而且可以單獨測試
     

 

架構關係全貌

 

Model 傳遞的關係

 

用 Interface 隔離實作

 

Web Api 的呼叫關係

 

2021-10-16 12:50

週期性執行的子執行緒範本

關於週期性執行的子執行緒有幾個要點:

  • 單次 Sleep 的時間不要太久,會影響程式的關閉
  • 不可以用 SpinWait 來做程式暫停,要用 Sleep 來暫停,這樣才會釋放 CPU
  • Error log 要注意會重複出現相同的 error
  • 有排除重複 error 時,要記得加上[錯誤解除]的 log,這樣才能知道程式是否有回到正常執行
  • 要執行的程式邏輯移到一個 method 裡,這樣可以避免 return 造成的迴圈中斷,也能分離邏輯的關注點


週期小於 10 秒的範本

private static readonly ILogger _log = LogManager.GetCurrentClassLogger();

private int _cycleMSec = 1000;
private bool _runFlag = false;
private string _prevError;

public void Start()
{
    if (_runFlag) { return; }
    _runFlag = true;

    var thread = new Thread(() =>
    {
        while (_runFlag)
        {
            /* 先睡可以避免在程式啟動時過於忙碌 */
            Thread.Sleep(_cycleMSec);

            try
            {
                /* 要執行的程式邏輯 */
                cycleHandler();
                if (_prevError != null) { _log.Info("錯誤結束"); }
                _prevError = null;
            }
            catch (Exception ex)
            {
                /* 避免相同的錯誤一直被記錄 */
                if (_prevError == ex.Message) { continue; }

                _prevError = ex.Message;
                _log.Fatal(ex, "執行錯誤");
            }
        }
    });

    thread.Start();
}

public void Stop()
{
    _runFlag = false;
}


private void cycleHandler()
{
    // 主要的邏輯程式寫在這裡
}


週期大於 10 秒的範本

private static readonly ILogger _log = LogManager.GetCurrentClassLogger();

private int _cycleSec = 30;
private bool _runFlag = false;
private string _prevError;
private DateTime _nextTime = DateTime.Now;

public void Start()
{
    if (_runFlag) { return; }
    _runFlag = true;

    var thread = new Thread(() =>
    {
        while (_runFlag)
        {
            /* 先睡可以避免在程式啟動時過於忙碌 */
            Thread.Sleep(1000);

            /* 檢查是否符合執行時間 */
            if (_nextTime > DateTime.Now) { continue; }

            /* 更新下一次的執行時間 */
            _nextTime = DateTime.Now.AddSeconds(_cycleSec);

            try
            {
                /* 要執行的程式邏輯 */
                cycleHandler();
                if (_prevError != null) { _log.Info("錯誤結束"); }
                _prevError = null;
            }
            catch (Exception ex)
            {
                /* 避免相同的錯誤一直被記錄 */
                if (_prevError == ex.Message) { continue; }

                _prevError = ex.Message;
                _log.Fatal(ex, "執行錯誤");
            }
        }
    });

    thread.Start();
}

public void Stop()
{
    _runFlag = false;
}


private void cycleHandler()
{
    // 主要的邏輯程式寫在這裡
}


停啟頻繁的範本

public enum CycleStatus
{
    Stop,
    Start,
    Stoping,
}

public CycleStatus RunStatus { get; private set; } = CycleStatus.Stop;


private static readonly ILogger _log = LogManager.GetCurrentClassLogger();
private int _cycleMSec = 1000;
private string _prevError;


public void Start()
{
    if (RunStatus != CycleStatus.Stop)
    { throw new Exception("程序還在進行中"); }

    RunStatus = CycleStatus.Start;

    var thread = new Thread(() =>
    {
        while (RunStatus == CycleStatus.Start)
        {
            /* 先睡可以避免在程式啟動時過於忙碌 */
            Thread.Sleep(_cycleMSec);

            try
            {
                /* 要執行的程式邏輯 */
                cycleHandler();
                if (_prevError != null) { _log.Info("錯誤結束"); }
                _prevError = null;
            }
            catch (Exception ex)
            {
                /* 避免相同的錯誤一直被記錄 */
                if (_prevError == ex.Message) { continue; }

                _prevError = ex.Message;
                _log.Fatal(ex, "執行錯誤");
            }
        }

        RunStatus = CycleStatus.Stop;
    });

    thread.Start();
}


public void Stop()
{
    if (RunStatus == CycleStatus.Stop)
    { throw new Exception("程序已經停止"); }

    if (RunStatus == CycleStatus.Stoping)
    { throw new Exception("程序正在停止"); }

    RunStatus = CycleStatus.Stoping;
}


private void cycleHandler()
{
    // 主要的邏輯程式寫在這裡
}

2020-07-06 13:18

C# MVC Cassette 隔離區快取找不到的錯誤

有使用 Cassette 的專案,在發布站台之後網頁開起來卻發生了 JS 跟 CSS 全部掉光,查看錯誤訊息時發現 Cassette 抓不到在隔離區的快取。

原因是當 JS 跟 CSS 有更新時,Cassette 會重新產生快取的 key,卻沒有產生隔離區的快取檔案,造成抓不到快取的問題,IIS pool 重啟也無效,Cassette 的管理畫面也進不去。

解決辦法就是呼叫 Bundles.RebuildCache(),最好方式就是在 Global.asax.cs 增加自動處裡。

protected void Application_Error(object sender, EventArgs e)
{
    /* 重建 Cassette 綑綁 */
    var ex = Server.GetLastError() as System.IO.FileNotFoundException;
    if (ex != null && ex.StackTrace.Contains("Cassette."))
    { Bundles.RebuildCache(); }
}
2019-07-19 16:46

WCF IP Filter

<!-- Web.config -->
<system.serviceModel>
  <extensions>
    <behaviorExtensions>
      <add name="ipFilter" type="XXX.XXX.IpFilterElement, XXX.XXX" />
    </behaviorExtensions>
  </extensions>

  <!-- .... -->

  <behaviors>
    <serviceBehaviors>
      <behavior>
        <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="true" />
        <ipFilter allow="192.168.1.0/24, 127.0.0.1" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Security.Authentication;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using JustWin.API.Extensions;


public class IpFilterElement : BehaviorExtensionElement
{
    [ConfigurationProperty("allow", IsRequired = true)]
    public virtual string Allow
    {
        get { return this["allow"] as string; }
        set { this["allow"] = value; }
    }

    public override Type BehaviorType
    {
        get { return typeof(IpFilterBehaviour); }
    }

    protected override object CreateBehavior()
    {
        return new IpFilterBehaviour(Allow);
    }
}




public class IpFilterBehaviour : IDispatchMessageInspector, IServiceBehavior
{
    private readonly List<IPAddressRange> _allowList;

    public IpFilterBehaviour(string allow)
    {
        _allowList = allow.Split(',').Select(x => new IPAddressRange(x)).ToList();
    }


    void IServiceBehavior.Validate(ServiceDescription service, ServiceHostBase host)
    {
    }

    void IServiceBehavior.AddBindingParameters(ServiceDescription service, ServiceHostBase host, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters)
    {
    }

    void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription service, ServiceHostBase host)
    {
        foreach (ChannelDispatcher dispatcher in host.ChannelDispatchers)
        foreach (EndpointDispatcher endpoint in dispatcher.Endpoints)
        {
            endpoint.DispatchRuntime.MessageInspectors.Add(this);
        }
    }



    object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        var remoteEndpoint = request.Properties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;

        var address = IPAddress.Parse(remoteEndpoint.Address);
        if(_allowList.Any(x => x.IsMatch(address))) { return null; }

        request = null;
        return new AuthenticationException($"IP address ({remoteEndpoint.Address}) is not allowed.");
    }


    void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
    {
        var ex = correlationState as Exception;
        if (ex == null) { return; }

        MessageFault messageFault = MessageFault.CreateFault(
            new FaultCode("Sender"),
            new FaultReason(ex.Message),
            ex,
            new NetDataContractSerializer()
        );

        reply = Message.CreateMessage(reply.Version, messageFault, null);
    }

}







public class IPAddressRange
{
    private readonly byte[] _rangeAddress;
    private readonly byte[] _rangeMask;

    public IPAddressRange(string ipAndMask)
    {
        string[] split = (ipAndMask + "/128").Split('/');

        var ip = IPAddress.Parse(split[0].Trim());

        int maskLength = int.Parse(split[1].Trim());
        if (ip.AddressFamily == AddressFamily.InterNetwork) { maskLength += 96; }

        _rangeMask = createMask(maskLength);

        _rangeAddress = ip.MapToIPv6().GetAddressBytes()
            .Select((x, i) => x & _rangeMask[i])
            .Select(x => (byte)x)
            .ToArray();
    }


    public bool IsMatch(IPAddress ip)
    {
        byte[] address = ip.MapToIPv6().GetAddressBytes();

        for (int i = 0; i < 16; i++)
        {
            if ((address[i] & _rangeMask[i]) != _rangeAddress[i]) { return false; }
        }

        return true;
    }



    private byte[] createMask(int length)
    {
        var mask = new byte[16];

        for (int i = 0; i < 16; i++)
        {
            mask[i] = 0xff;
            if (length > -8) { length -= 8; }
            if (length < 0) { mask[i] = (byte)(mask[i] << -length); }
        }
        return mask;
    }
}
2018-05-12 23:58

Windows Server 磁碟清理


磁碟清理
DISM.exe /online /Cleanup-Image /StartComponentCleanup

清理系統 Service Pack 備份檔案
DISM.exe /Online /Cleanup-Image /SPSuperseded
2018-05-12 23:53

Visual Studio 快捷鍵

Ctrl + L刪除行
Ctrl + X剪下行(游標不選取文字)
Ctrl + C複製行(游標不選取文字)
Ctrl + J呼叫出類別成員
Ctrl + K,C註解選取範圍
Ctrl + K,U取消註解選取範圍
Ctrl + R,R重新命名變數
F2重新命名變數
Ctrl + Enter上方插入一列
Ctrl + Shift + Enter下方插入一列
Ctrl + 减號回到上次游標位置
CTRL + SHIFT + 减號反之
Ctrl + F3找當前選取
F3找下一個
Shift + F3找上一個
Ctrl + F尋找文字
Ctrl + Shift + F跨檔案尋找文字
Ctrl + H取代文字
Ctrl + Shift + H跨檔案取代文字
Ctrl + J顯示物件的成員清單
Ctrl + K, D格式化文件
Ctrl + K, F格式化選取範圍
Ctrl + E, S顯示空白字元
Ctrl + E, \刪除行尾空白
Ctrl + Alt + ]對齊等號
Ctrl + ]切換至對應刮號
F12轉至定義
Ctrl + F12轉至實做
Alt + F12查看定義
F5偵錯建置
Ctrl + F5建置
Ctrl + U小寫
Ctrl + Shift + U大寫
Alt + 上選取行上移
Alt + 下選取行下移
Ctrl + K, K切換書籤
Ctrl + .開啟智慧標籤選單
Shift + Alt + F10開啟智慧標籤選單
2017-09-01 11:27

SQL Server Management Studio 資料表設計模式顯示欄位描述


開啟登錄檔編輯器 regedit.exe
搜尋 SSVPropViewColumnsSQL70 及 SSVPropViewColumnsSQL80
將值從 1,2,6; 改為 1,2,6,17;

各個屬性欄位代號:
1:  Column Name
2:  Data Type
3:  Length
4:  Precision
5:  Scale
6:  Allow Nulls
7:  Default Value
8:  Identity
9:  Identity Seed
10: Identity Increment
11: Row GUID
12: Nullable
13: Condensed Type
14: Not for Replication
15: Formula
16: Collation
17: Description



2016-04-07 12:07

C# MVC + Cassette

Cassette 與 MVC 預設的 Bundle 最大的差異就是會自動轉換 CSS 的圖片網址。缺點就是開發時 F5 啟動專案會變慢。


NuGet 安裝

  • Cassette.Aspnet
  • Cassette.Views
  • Cassette


Web.config


基本上安裝完就會自動配置以下設定,但可以在確認一下。

<configuration>
  <configSections>
    <section name="cassette"
             type="Cassette.CassetteConfigurationSection, Cassette"
             requirePermission="false"
    />
  </configSections>
  <system.web>
    <pages>
      <namespaces>
        <add namespace="Cassette.Views" />
      </namespaces>
    </pages>
    <httpModules>
      <add name="CassetteHttpModule"
           type="Cassette.Aspnet.CassetteHttpModule, Cassette.Aspnet"
      />
    </httpModules>
    <httpHandlers>
      <!-- 用來處理綑綁打包的處理器 -->
      <add path="cassette.axd"
           verb="*"
           type="Cassette.Aspnet.CassetteHttpHandler, Cassette.Aspnet"
       />
    </httpHandlers>
  </system.web>
  <system.webServer>
    <!-- 保留 httpHandlers(IIS6) 及 handlers(IIS7) 需要關閉驗證 -->
    <validation validateIntegratedModeConfiguration="false" />
    <modules>
      <add name="CassetteHttpModule"
           type="Cassette.Aspnet.CassetteHttpModule, Cassette.Aspnet"
      />
    </modules>
    <handlers>
      <!-- 用來處理綑綁打包的處理器 -->
      <add name="CassetteHttpHandler"
           path="cassette.axd"
           verb="*"
           allowPathInfo="true"
           preCondition="integratedMode"
           type="Cassette.Aspnet.CassetteHttpHandler, Cassette.Aspnet"
       />
    </handlers>
  </system.webServer>

  <!-- For configuration options: http://getcassette.net/documentation/v2/web-config -->
  <cassette rewriteHtml="false" />
</configuration>



Views/Web.config


增加給 cshtml 用的 namespace

<configuration>
  <system.web.webPages.razor>
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="Cassette.Views" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>
</configuration>



CassetteConfiguration.cs


public class CassetteBundleConfiguration : IConfiguration<BundleCollection>
{
    public void Configure(BundleCollection bundles)
    {
        bundles.AddPerIndividualFile<StylesheetBundle>("Content");
        bundles.AddPerIndividualFile<ScriptBundle>("Scripts");

        bundles.Add<StylesheetBundle>("Styles/Bundles", new string[] 
        {
            "~/Scripts/bootstrap/css/bootstrap.css",
            "~/Scripts/bootstrap/css/bootstrap-theme.css",
            //...
        });

        bundles.Add<ScriptBundle>("Scripts/Bundles", new string[] 
        {
            "~/Scripts/jquery/jquery-1.12.2.js",
            "~/Scripts/jquery.validate/jquery.validate-1.12.0.js",
            //...
        });
    }
}



_Layout.cshtml


<!DOCTYPE html>
@{
    Bundles.Reference("Styles/Bundles");
    Bundles.Reference("Scripts/Bundles");
}
<html>
<head>
    @Bundles.RenderStylesheets()
</head>
<body id="DialogLayout">
    @Bundles.RenderScripts()
</body>
</html>



Release Mode


當 Web.config 的 compilation debug="false" 就會壓縮綑綁。

<compilation debug="false" targetFramework="4.5" />
2015-03-13 13:12

[Java] Ant zip 解壓縮筆記

<unzip dest="./target_dir">

    <!-- 來源的壓縮檔 -->
    <fileset dir="lib">
        <include name="tiles-jsp-*.jar"/>
    </fileset>

    <!-- 要解出的檔案 -->
    <patternset>
        <include name="**/*.tld"/>
    </patternset>

    <!-- 解出路徑的轉換 -->
    <mapper type="flatten"/>

</unzip>

參考文件:
Apache Ant™ User Manual : Unzip Task
Apache Ant™ User Manual : Mapper
2015-03-13 11:59

[Java] Reflection 筆記

@SuppressWarnings("unused")
Object obj = new Object() {
    String id = "123";
    public String name = "Jax";
};

Class<?> cl = obj.getClass();

for (Field field : cl.getFields()) {
    System.out.printf("%s = %s {%s}\n",
        field.getName(), field.get(obj), field.getType());
}

System.out.println("=======================");

for (Field field : cl.getDeclaredFields()) {
    System.out.printf("%s = %s {%s}\n",
        field.getName(), field.get(obj), field.getType());
}

Output:
name = Jax {class java.lang.String}
=======================
id = 123 {class java.lang.String}
name = Jax {class java.lang.String}
2015-03-10 15:18

[Java] 產生驗證碼圖片

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Random;

import javax.imageio.ImageIO;

public class TestCaptchaImage {

    public static void main(String[] args) throws Exception {
        String captcha = "09875";

        int width = 55;
        int height = 20;
        Color fontColor = new Color(36, 85, 92); /*文字顏色*/
        Color lineColor = new Color(65, 161, 175); /*線條顏色*/
        Color bgColor = new Color(208, 226, 229); /*底色*/

        Random random = new Random();
        random.setSeed(System.currentTimeMillis());

        BufferedImage image = new BufferedImage(
            width, height, BufferedImage.TYPE_INT_RGB
        );
        Graphics g = image.getGraphics();

        /* 底色 */
        g.setColor(bgColor);
        g.fillRect(0, 0, width, height);

        /* 畫線 */
        g.setColor(lineColor);
        for (int i=0; i < 20; i++) {
            g.drawLine(
                random.nextInt(width), 0,
                random.nextInt(width), height
            );
        }

        /* 畫出文字 */
        g.setFont(new Font("Atlantic Inline", Font.BOLD, 20));
        g.setColor(fontColor);
        g.drawString(captcha, 0, 18);

        /* 畫線 */
        g.setColor(lineColor);
        for (int i=0; i < 4; i++) {
            g.drawLine(
                0, random.nextInt(height),
                width, random.nextInt(height)
            );
        }

        g.dispose();

        ImageIO.write(image, "png", new File("captcha_image.png"));
    }
}

參考文件:
Graphics (Java 2 Platform SE 6)
2015-03-06 17:12

[Java] 製作縮圖筆記

package test_image;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;

public class TestImageResize {

    private static int targetWidth = 120;
    private static int targetHeight = 80;
    private static double targetRate = (double) targetWidth / targetHeight;

    public static void main(String[] args) throws Exception {

        System.out.printf("Target w:%s, h:%s, r:%s\n", 
            targetWidth, targetHeight, targetRate);


        BufferedImage image = ImageIO.read(new File("input.jpg"));

        int type = image.getType();
        if(type == 0) { type = BufferedImage.TYPE_INT_ARGB; }

        int width = image.getWidth();
        int height = image.getHeight();
        double rate = (double) width / height;

        System.out.printf("Source w:%s, h:%s, r:%s\n", width, height, rate);



        /* 等比例縮小至指定大小內 */
        int rWidth = targetWidth;
        int rHeight = targetHeight;

        if(width < targetWidth && height < targetHeight) {
            rWidth = width;
            rHeight = height;
        } else if(rate > targetRate) {
            rHeight = (int) (targetWidth / rate);
        } else {
            rWidth = (int) (targetHeight * rate);
        }
        System.out.printf("Resize w:%s, h:%s\n", rWidth, rHeight);

        BufferedImage resize1 = new BufferedImage(rWidth, rHeight, type);
        Graphics g1 = resize1.getGraphics();
        g1.drawImage(image, 0, 0, rWidth, rHeight, null);
        g1.dispose();

        ImageIO.write(resize1, "jpg", new File("output_1.jpg"));




        /* 等比例縮小填滿指定大小 */
        BufferedImage resize2 = new BufferedImage(targetWidth,targetHeight,type);
        Graphics g2 = resize2.getGraphics();

        int startX = 0;
        int startY = 0;
        int size = 0;

        if(rate > targetRate) {
            startX = (int) (width - height * targetRate) / 2;
            size = height;
        } else {
            startY = (int) (height - width / targetRate) / 2;
            size = width;
        }
        System.out.printf("x:%s, y:%s, size:%s\n", startX, startY, size);

        g2.drawImage(
            image,
            0, 0, targetWidth, targetHeight,
            startX, startY, (size + startX), (size + startY),
            null
        );

        g2.dispose();

        ImageIO.write(resize2, "jpg", new File("output_2.jpg"));
    }
}

參考文件:
Graphics (Java 2 Platform SE 6)
2015-03-06 15:33

[Java] Jsoup 筆記

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class TestJsoup {

    public static void main(String[] args) throws Exception {

        String url = "http://epg.dishstar.net/calendar.php?s=DISC&d=1";

        // Document doc = Jsoup.parse(new URL(url), 5000);
        Document doc = Jsoup.connect(url)
                            .userAgent("Mozilla/5.0")
                            .timeout(5000).get();

        Elements trs = doc.select("table tr");
        for (Element tr : trs) {
            String time = tr.select("td:eq(0)").text();
            String title = tr.select("td:eq(1)").text();

            System.out.println(time + " <=> " + title);
        }
    }
}
2015-03-04 11:33

[Java] FileFilter 筆記

File dir = new File("D:/log");

File[] list = dir.listFiles(new FileFilter(){
    public boolean accept(File file) {
        return file.getName().endsWith(".txt");
    }
});

for(File file : list){
    System.out.println(file.getAbsoluteFile());
}

// 004.txt
2015-03-04 11:27

[Java] 取得本地端 IP 與 MAC

import java.net.InetAddress;
import java.net.NetworkInterface;

public class TestInetAddress {

    public static void main(String[] args) throws Exception {
        InetAddress ip = InetAddress.getLocalHost();

        System.out.println("Current IP address : " + ip.getHostAddress());
        // Current IP address : 192.168.0.109

        System.out.println(ip.getCanonicalHostName());
        // 192.168.0.109

        System.out.println(ip.getHostName());
        // jaxhu-PC



        NetworkInterface network = NetworkInterface.getByInetAddress(ip);

        byte[] mac = network.getHardwareAddress();

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < mac.length; i++) {
            sb.append(String.format("%s%02X", (i > 0 ? "-" : ""), mac[i]));
        }

        System.out.println("Current MAC address : " + sb.toString());
        // Current MAC address : 38-2C-4A-B4-C3-24

        System.out.println(network.getDisplayName());
        // Realtek PCIe GBE Family Controller

        System.out.println(network.getName());
        // eth3
    }
}

參考自:How to get MAC address in Java : Mkyong
2015-03-04 11:20

[Java] 執行 shell command

Process p = Runtime.getRuntime().exec("ping -n 3 google.com");
p.waitFor();

Scanner sc = new Scanner(p.getInputStream(), "MS950");
String output = sc.useDelimiter("\\Z").next();
sc.close();

System.out.println("exitValue: " + p.exitValue());
System.out.println(output);

Output:
exitValue: 0

Ping google.com [173.194.72.138] (使用 32 位元組的資料):
回覆自 173.194.72.138: 位元組=32 時間=21ms TTL=48
回覆自 173.194.72.138: 位元組=32 時間=20ms TTL=48
回覆自 173.194.72.138: 位元組=32 時間=18ms TTL=48

173.194.72.138 的 Ping 統計資料:
    封包: 已傳送 = 3,已收到 = 3, 已遺失 = 0 (0% 遺失),
大約的來回時間 (毫秒):
    最小值 = 18ms,最大值 = 21ms,平均 = 19ms