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:33
[Python] Flask Log 配置
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 調用
附註
- 同一層之間不可以互相參考,這容易發生循環參考,例如:
- Controller 呼叫 Controller
- Service 呼叫 Service
- Dao 呼叫 Dao
- 同一層之間有相同邏輯可以抽離到 Support 類
- 建議定義命名規範,讓每一個人寫出來的程式像是同一個人寫的,好處是降低支援或接手的人的困難度
- 任何第三方的 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 的管理畫面也進不去。
解決辦法就是呼叫
原因是當 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 | 開啟智慧標籤選單 |
分類:
工作備忘,
Visual Studio
0
回應
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 啟動專案會變慢。
基本上安裝完就會自動配置以下設定,但可以在確認一下。
增加給 cshtml 用的 namespace
當 Web.config 的 compilation debug="false" 就會壓縮綑綁。
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
訂閱:
文章 (Atom)