posted @ 2012-07-13 22:07 小胡子 閱讀(2785) | 評(píng)論 (1) | 編輯 收藏
posted @ 2012-07-12 22:54 小胡子 閱讀(137) | 評(píng)論 (0) | 編輯 收藏
posted @ 2012-07-12 22:53 小胡子 閱讀(236) | 評(píng)論 (0) | 編輯 收藏
Google 圖片搜索功能
在谷歌圖片搜索中, 用戶(hù)可以上傳一張圖片, 谷歌顯示因特網(wǎng)中與此圖片相同或者相似的圖片.
比如我上傳一張照片試試效果:
原理講解
參考Neal Krawetz博士的這篇文章, 實(shí)現(xiàn)這種功能的關(guān)鍵技術(shù)叫做"感知哈希算法"(Perceptual Hash Algorithm), 意思是為圖片生成一個(gè)指紋(字符串格式), 兩張圖片的指紋越相似, 說(shuō)明兩張圖片就越相似. 但關(guān)鍵是如何根據(jù)圖片計(jì)算出"指紋"呢? 下面用最簡(jiǎn)單的步驟來(lái)說(shuō)明一下原理:
第一步 縮小圖片尺寸
將圖片縮小到8x8的尺寸, 總共64個(gè)像素. 這一步的作用是去除各種圖片尺寸和圖片比例的差異, 只保留結(jié)構(gòu)、明暗等基本信息.
第二步 轉(zhuǎn)為灰度圖片
將縮小后的圖片, 轉(zhuǎn)為64級(jí)灰度圖片.
第三步 計(jì)算灰度平均值
計(jì)算圖片中所有像素的灰度平均值
第四步 比較像素的灰度
將每個(gè)像素的灰度與平均值進(jìn)行比較, 如果大于或等于平均值記為1, 小于平均值記為0.
第五步 計(jì)算哈希值
將上一步的比較結(jié)果, 組合在一起, 就構(gòu)成了一個(gè)64位的二進(jìn)制整數(shù), 這就是這張圖片的指紋.
第六步 對(duì)比圖片指紋
得到圖片的指紋后, 就可以對(duì)比不同的圖片的指紋, 計(jì)算出64位中有多少位是不一樣的. 如果不相同的數(shù)據(jù)位數(shù)不超過(guò)5, 就說(shuō)明兩張圖片很相似, 如果大于10, 說(shuō)明它們是兩張不同的圖片.
代碼實(shí)現(xiàn) (C#版本)
下面我用C#代碼根據(jù)上一節(jié)所闡述的步驟實(shí)現(xiàn)一下.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | using System; using System.IO; using System.Drawing; namespace SimilarPhoto { class SimilarPhoto { Image SourceImg; public SimilarPhoto( string filePath) { SourceImg = Image.FromFile(filePath); } public SimilarPhoto(Stream stream) { SourceImg = Image.FromStream(stream); } public String GetHash() { Image image = ReduceSize(); Byte[] grayValues = ReduceColor(image); Byte average = CalcAverage(grayValues); String reslut = ComputeBits(grayValues, average); return reslut; } // Step 1 : Reduce size to 8*8 private Image ReduceSize( int width = 8, int height = 8) { Image image = SourceImg.GetThumbnailImage(width, height, () => { return false ; }, IntPtr.Zero); return image; } // Step 2 : Reduce Color private Byte[] ReduceColor(Image image) { Bitmap bitMap = new Bitmap(image); Byte[] grayValues = new Byte[image.Width * image.Height]; for ( int x = 0; x<image.Width; x++) for ( int y = 0; y < image.Height; y++) { Color color = bitMap.GetPixel(x, y); byte grayValue = ( byte )((color.R * 30 + color.G * 59 + color.B * 11) / 100); grayValues[x * image.Width + y] = grayValue; } return grayValues; } // Step 3 : Average the colors private Byte CalcAverage( byte [] values) { int sum = 0; for ( int i = 0; i < values.Length; i++) sum += ( int )values[i]; return Convert.ToByte(sum / values.Length); } // Step 4 : Compute the bits private String ComputeBits( byte [] values, byte averageValue) { char [] result = new char [values.Length]; for ( int i = 0; i < values.Length; i++) { if (values[i] < averageValue) result[i] = '0' ; else result[i] = '1' ; } return new String(result); } // Compare hash public static Int32 CalcSimilarDegree( string a, string b) { if (a.Length != b.Length) throw new ArgumentException(); int count = 0; for ( int i = 0; i < a.Length; i++) { if (a[i] != b[i]) count++; } return count; } } } |
谷歌服務(wù)器里的圖片數(shù)量是百億級(jí)別的, 我電腦里的圖片數(shù)量當(dāng)然沒(méi)法比, 但以前做過(guò)爬蟲(chóng)程序, 電腦里有40,000多人的頭像照片, 就拿它們作為對(duì)比結(jié)果吧! 我計(jì)算出這些圖片的"指紋", 放在一個(gè)txt文本中, 格式如下.
用ASP.NET寫(xiě)一個(gè)簡(jiǎn)單的頁(yè)面, 允許用戶(hù)上傳一張圖片, 后臺(tái)計(jì)算出該圖片的指紋, 并與txt文本中各圖片的指紋對(duì)比, 整理出結(jié)果顯示在頁(yè)面中, 效果如下:
本文地址: http://www.cnblogs.com/technology/archive/2012/07/12/Perceptual-Hash-Algorithm.html
原文:http://www.cnblogs.com/technology/archive/2012/07/12/Perceptual-Hash-Algorithm.html
posted @ 2012-07-12 22:48 小胡子 閱讀(285) | 評(píng)論 (0) | 編輯 收藏
Flyweight定義:
避免大量擁有相同內(nèi)容的小類(lèi)的開(kāi)銷(xiāo)(如耗費(fèi)內(nèi)存),使大家共享一個(gè)類(lèi)(元類(lèi)).
為什么使用?
面向?qū)ο笳Z(yǔ)言的 原則就是一切都是對(duì)象,但是如果真正使用起來(lái),有時(shí)對(duì)象數(shù)可能顯得很龐大,比如,字處理軟件,如果以每個(gè)文字都作為一個(gè)對(duì)象,幾千個(gè)字,對(duì)象數(shù)就是幾千, 無(wú)疑耗費(fèi)內(nèi)存,那么我們還是要"求同存異",找出這些對(duì)象群的共同點(diǎn),設(shè)計(jì)一個(gè)元類(lèi),封裝可以被共享的類(lèi),另外,還有一些特性是取決于應(yīng)用 (context),是不可共享的,這也Flyweight中兩個(gè)重要概念內(nèi)部狀態(tài)intrinsic和外部狀態(tài)extrinsic之分.
說(shuō)白點(diǎn),就是先捏一個(gè)的原始模型,然后隨著不同場(chǎng)合和環(huán)境,再產(chǎn)生各具特征的具體模型,很顯然,在這里需要 產(chǎn)生不同的新對(duì)象,所以Flyweight模式中常出現(xiàn)Factory模式.Flyweight的內(nèi)部狀態(tài)是用來(lái)共享的,Flyweight factory負(fù)責(zé)維護(hù)一個(gè)Flyweight pool(模式池)來(lái)存放內(nèi)部狀態(tài)的對(duì)象.
Flyweight模式是一個(gè)提高程序效率和性能的模式,會(huì)大大加快程序的運(yùn)行速度.應(yīng)用場(chǎng)合很多:比如你要從一個(gè)數(shù)據(jù)庫(kù)中讀取一系列字符串,這些字符串中有許多是重復(fù)的,那么我們可以將這些字符串儲(chǔ)存在Flyweight池(pool)中.
如何使用?
我們先從Flyweight抽象接口開(kāi)始:
public interface Flyweight //用于本模式的抽象數(shù)據(jù)類(lèi)型(自行設(shè)計(jì)) |
下面是接口的具體實(shí)現(xiàn)(ConcreteFlyweight) ,并為內(nèi)部狀態(tài)增加內(nèi)存空間, ConcreteFlyweight必須是可共享的,它保存的任何狀態(tài)都必須是內(nèi)部(intrinsic),也就是說(shuō),ConcreteFlyweight必須和它的應(yīng)用環(huán)境場(chǎng)合無(wú)關(guān).
public class ConcreteFlyweight implements Flyweight { } |
當(dāng)然,并不是所有的Flyweight具體實(shí)現(xiàn)子類(lèi)都需要被共享的,所以還有另外一種不共享的ConcreteFlyweight:
public class UnsharedConcreteFlyweight implements Flyweight { public void operation( ExtrinsicState state ) { } } |
Flyweight factory負(fù)責(zé)維護(hù)一個(gè)Flyweight池(存放內(nèi)部狀態(tài)),當(dāng)客戶(hù)端請(qǐng)求一個(gè)共享Flyweight時(shí),這個(gè)factory首先搜索池中是否已經(jīng) 有可適用的,如果有,factory只是簡(jiǎn)單返回送出這個(gè)對(duì)象,否則,創(chuàng)建一個(gè)新的對(duì)象,加入到池中,再返回送出這個(gè)對(duì)象.池
public class FlyweightFactory { Flyweight flyweight = (Flyweight) flyweights.get(key); if( flyweight == null ) { return flyweight; |
至此,Flyweight模式的基本框架已經(jīng)就緒,我們看看如何調(diào)用:
FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight( "Fred" );
Flyweight fly2 = factory.getFlyweight( "Wilma" );
......
從調(diào)用上看,好象是個(gè)純粹的Factory使用,但奧妙就在于Factory的內(nèi)部設(shè)計(jì)上.
Flyweight模式在XML等數(shù)據(jù)源中應(yīng)用
我們上面已經(jīng)提到,當(dāng)大量從數(shù)據(jù)源中讀取字符串,其中肯定有重復(fù)的,那么我們使用Flyweight模式可以提高效率,以唱片CD為例,在一個(gè)XML文件中,存放了多個(gè)CD的資料.
每個(gè)CD有三個(gè)字段:
1.出片日期(year)
2.歌唱者姓名等信息(artist)
3.唱片曲目 (title)
其中,歌唱者姓名有可能重復(fù),也就是說(shuō),可能有同一個(gè)演唱者的多個(gè)不同時(shí)期 不同曲目的CD.我們將"歌唱者姓名"作為可共享的ConcreteFlyweight.其他兩個(gè)字段作為UnsharedConcreteFlyweight.
首先看看數(shù)據(jù)源XML文件的內(nèi)容:
<cd> <cd> <cd> ....... </collection> |
雖然上面舉例CD只有3張,CD可看成是大量重復(fù)的小類(lèi),因?yàn)槠渲谐煞种挥腥齻€(gè)字段,而且有重復(fù)的(歌唱者姓名).
CD就是類(lèi)似上面接口 Flyweight:
private String title; |
將"歌唱者姓名"作為可共享的ConcreteFlyweight:
public class Artist { //內(nèi)部狀態(tài) // note that Artist is immutable. Artist(String n){ |
再看看Flyweight factory,專(zhuān)門(mén)用來(lái)制造上面的可共享的ConcreteFlyweight:Artist
public class ArtistFactory { Hashtable pool = new Hashtable(); Artist result; |
當(dāng)你有幾千張甚至更多CD時(shí),Flyweight模式將節(jié)省更多空間,共享的flyweight越多,空間節(jié)省也就越大.
posted @ 2012-07-11 21:58 小胡子 閱讀(215) | 評(píng)論 (0) | 編輯 收藏
posted @ 2012-07-11 21:57 小胡子 閱讀(522) | 評(píng)論 (0) | 編輯 收藏
1. 概述
將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象,從而使你可用不同的請(qǐng)求對(duì)客戶(hù)進(jìn)行參數(shù)化; 對(duì)請(qǐng)求排隊(duì)或記錄請(qǐng)求日志,以及支持可撤銷(xiāo)的操作。
2. 解決的問(wèn)題
在軟件系統(tǒng)中,行為請(qǐng)求者與行為實(shí)現(xiàn)者通常是一種緊耦合的關(guān)系,但某些場(chǎng)合,比如需要對(duì)行為進(jìn)行記錄、撤銷(xiāo)或重做、事務(wù)等處理時(shí),這種無(wú)法抵御變化的緊耦合的設(shè)計(jì)就不太合適。
3. 模式中角色
3.1 抽象命令(Command):定義命令的接口,聲明執(zhí)行的方法。
3.2 具體命令(ConcreteCommand):具體命令,實(shí)現(xiàn)要執(zhí)行的方法,它通常是“虛”的實(shí)現(xiàn);通常會(huì)有接收者,并調(diào)用接收者的功能來(lái)完成命令要執(zhí)行的操作。
3.3 接收者(Receiver):真正執(zhí)行命令的對(duì)象。任何類(lèi)都可能成為一個(gè)接收者,只要能實(shí)現(xiàn)命令要求實(shí)現(xiàn)的相應(yīng)功能。
3.4 調(diào)用者(Invoker):要求命令對(duì)象執(zhí)行請(qǐng)求,通常會(huì)持有命令對(duì)象,可以持有很多的命令對(duì)象。這個(gè)是客戶(hù)端真正觸發(fā)命令并要求命令執(zhí)行相應(yīng)操作的地方,也就是說(shuō)相當(dāng)于使用命令對(duì)象的入口。
3.5 客戶(hù)端(Client):命令由客戶(hù)端來(lái)創(chuàng)建,并設(shè)置命令的接收者。
4. 模式解讀
4.1 命令模式的類(lèi)圖
4.2 命令模式的實(shí)現(xiàn)代碼
/// 接收者類(lèi),知道如何實(shí)施與執(zhí)行一個(gè)請(qǐng)求相關(guān)的操作,任何類(lèi)都可能作為一個(gè)接收者。
/// </summary>
public class Receiver
{
/// <summary>
/// 真正的命令實(shí)現(xiàn)
/// </summary>
public void Action()
{
Console.WriteLine("Execute request!");
}
}
/// <summary>
/// 抽象命令類(lèi),用來(lái)聲明執(zhí)行操作的接口
/// </summary>
public interface ICommand
{
void Execute();
}
/// <summary>
/// 具體命令類(lèi),實(shí)現(xiàn)具體命令。
/// </summary>
public class ConcereteCommand : ICommand
{
// 具體命令類(lèi)包含有一個(gè)接收者,將這個(gè)接收者對(duì)象綁定于一個(gè)動(dòng)作
private Receiver receiver;
public ConcereteCommand(Receiver receiver)
{
this.receiver = receiver;
}
/// <summary>
/// 說(shuō)這個(gè)實(shí)現(xiàn)是“虛”的,因?yàn)樗峭ㄟ^(guò)調(diào)用接收者相應(yīng)的操作來(lái)實(shí)現(xiàn)Execute的
/// </summary>
public void Execute()
{
receiver.Action();
}
}
/// <summary>
/// 調(diào)度類(lèi),要求該命令執(zhí)行這個(gè)請(qǐng)求
/// </summary>
public class Invoker
{
private ICommand command;
/// <summary>
/// 設(shè)置命令
/// </summary>
/// <param name="command"></param>
public void SetCommand(ICommand command)
{
this.command = command;
}
/// <summary>
/// 執(zhí)行命令
/// </summary>
public void ExecuteCommand()
{
command.Execute();
}
}
{
static void Main(string[] args)
{
Receiver receiver = new Receiver();
ICommand command = new ConcereteCommand(receiver);
Invoker invoker = new Invoker();
invoker.SetCommand(command);
invoker.ExecuteCommand();
Console.Read();
}
}
4.4 模式分析
4.4.1 本質(zhì):對(duì)命令進(jìn)行封裝,將發(fā)出命令與執(zhí)行命令的責(zé)任分開(kāi)。
4.4.2 每一個(gè)命令都是一個(gè)操作:請(qǐng)求的一方發(fā)出請(qǐng)求,要求執(zhí)行一個(gè)操作;接收的一方收到請(qǐng)求,并執(zhí)行操作。
4.4.3 請(qǐng)求方和接收方獨(dú)立開(kāi)來(lái),使得請(qǐng)求的一方不必知道接收請(qǐng)求的一方的接口,更不必知道請(qǐng)求是怎么被接收,以及操作是否被執(zhí)行、何時(shí)被執(zhí)行,以及是怎么被執(zhí)行的。
4.4.4 使請(qǐng)求本身成為一個(gè)對(duì)象,這個(gè)對(duì)象和其它對(duì)象一樣可以被存儲(chǔ)和傳遞。
4.4.5 命令模式的關(guān)鍵在于引入了抽象命令接口,且發(fā)送者針對(duì)抽象命令接口編程,只有實(shí)現(xiàn)了抽象命令接口的具體命令才能與接收者相關(guān)聯(lián)。
5. 模式總結(jié)
5.1 優(yōu)點(diǎn)
5.1.1 解除了請(qǐng)求者與實(shí)現(xiàn)者之間的耦合,降低了系統(tǒng)的耦合度。
5.1.2 對(duì)請(qǐng)求排隊(duì)或記錄請(qǐng)求日志,支持撤銷(xiāo)操作。
5.1.3 可以容易地設(shè)計(jì)一個(gè)組合命令。
5.1.4 新命令可以容易地加入到系統(tǒng)中。
5.2 缺點(diǎn)
5.2.1 因?yàn)獒槍?duì)每一個(gè)命令都需要設(shè)計(jì)一個(gè)具體命令類(lèi),使用命令模式可能會(huì)導(dǎo)致系統(tǒng)有過(guò)多的具體命令類(lèi)。
5.3 適用場(chǎng)景
5.3.1 當(dāng)需要對(duì)行為進(jìn)行“記錄、撤銷(xiāo)/重做”等處理時(shí)。
5.3.2 系統(tǒng)需要將請(qǐng)求者和接收者解耦,使得調(diào)用者和接收者不直接交互。
5.3.3 系統(tǒng)需要在不同時(shí)間指定請(qǐng)求、請(qǐng)求排隊(duì)和執(zhí)行請(qǐng)求。
5.3.4 系統(tǒng)需要將一組操作組合在一起,即支持宏命令。
6. 應(yīng)用舉例:銀行帳號(hào)的存款、提款
6.1 類(lèi)圖
6.2 代碼實(shí)現(xiàn)
/// 銀行帳號(hào)
/// </summary>
public class Account
{
/// <summary>
/// 帳號(hào)總金額
/// </summary>
private decimal totalAmount { get; set; }
/// <summary>
/// 存錢(qián)
/// </summary>
/// <param name="amount"></param>
public void MoneyIn(decimal amount)
{
this.totalAmount += amount;
}
/// <summary>
/// 取錢(qián)
/// </summary>
/// <param name="amount"></param>
public void MoneyOut(decimal amount)
{
this.totalAmount -= amount;
}
public decimal GetTotalAmout()
{
return totalAmount;
}
}
public abstract class Command
{
protected Account account;
public Command(Account account)
{
this.account = account;
}
public abstract void Execute();
}
/// <summary>
/// 存款命令
/// </summary>
public class MoneyInCommand : Command
{
private decimal amount;
public MoneyInCommand(Account account, decimal amount)
: base(account)
{
this.amount = amount;
}
/// <summary>
/// 實(shí)現(xiàn)存錢(qián)命令
/// </summary>
public override void Execute()
{
account.MoneyIn(amount);
}
}
/// <summary>
/// 取款命令類(lèi)
/// </summary>
public class MoneyOutCommand : Command
{
private decimal amount;
public MoneyOutCommand(Account account, decimal amount)
: base(account)
{
this.amount = amount;
}
/// <summary>
/// 實(shí)現(xiàn)取錢(qián)命令
/// </summary>
public override void Execute()
{
account.MoneyOut(amount);
}
}
public class Invoker
{
private Command command;
public void SetCommand(Command command)
{
this.command = command;
}
public void ExecuteCommand()
{
command.Execute();
}
}
{
static void Main(string[] args)
{
// 創(chuàng)建銀行帳號(hào)
Account account = new Account();
// 創(chuàng)建一個(gè)存入500元的命令
Command commandIn = new MoneyInCommand(account,500);
// 創(chuàng)建一個(gè)調(diào)度者
BankAccount.Invoker invoker = new BankAccount.Invoker();
// 設(shè)置存錢(qián)命令
invoker.SetCommand(commandIn);
// 執(zhí)行
invoker.ExecuteCommand();
Console.WriteLine("The current amount is " + account.GetTotalAmout().ToString("N2"));
// 再次存入500
Command commandIn2 = new MoneyInCommand(account, 500);
invoker.SetCommand(commandIn2);
invoker.ExecuteCommand();
Console.WriteLine("The current amount is " + account.GetTotalAmout().ToString("N2"));
// 取出300
Command commandOut = new MoneyOutCommand(account, 300);
invoker.SetCommand(commandOut);
invoker.ExecuteCommand();
Console.WriteLine("The current amount is " + account.GetTotalAmout().ToString("N2"));
Console.Read();
}
}
轉(zhuǎn)自:
posted @ 2012-07-11 21:54 小胡子 閱讀(161) | 評(píng)論 (0) | 編輯 收藏
需求場(chǎng)景:
1. 頁(yè)面內(nèi)有多級(jí)iframe嵌套。
2. iframe內(nèi)部某些按鈕點(diǎn)擊后需要彈出浮層。
3. 浮層需要將整個(gè)瀏覽器窗口遮罩,且浮層位于瀏覽窗口中部。
效果如下:
解決思路:
- 頂層頁(yè)面內(nèi)預(yù)留用于顯示浮層的div(命名為popdiv),且該div內(nèi)有一預(yù)留的iframe,該iframe用于加載浮層內(nèi)容,命名為popiframe
- 提供可以直接訪(fǎng)問(wèn)浮層內(nèi)容的url連接
- iframe觸發(fā)顯示浮層事件時(shí),通過(guò)window.top 設(shè)置頂層窗口的popiframe.src。
- 通過(guò)計(jì)算浮層內(nèi)容的長(zhǎng)寬及當(dāng)前窗口的長(zhǎng)寬設(shè)置popdiv的位置,使其在窗口中央顯示。
實(shí)現(xiàn):
頂層頁(yè)面相關(guān)html代碼:
<div id="mask" style="display:none;"></div>
<div id="id_popdiv" style="display:none;" class="popup"> <iframe id="id_popiframe" src="" frameborder="0" scrolling="no" width="100%" height="100%"> </iframe> </div> |
iframe有點(diǎn)擊按鈕的html代碼
<script type="text/javascript" src="js/popmanager.js"></script>
<a href="javascript:pop('popcontenturl',782,600);" class="link" rel="1">show pop content</a><br /> |
popcontenturl 頁(yè)面中的關(guān)閉的主要代碼:
<script type="text/javascript" src="js/showhide.js" charset="utf-8"></script> <div class="pop_container"> <a href="javascript:unpop();" title="關(guān)閉" class="close"></a> <h2 class="title">浮層標(biāo)題</h2> <div class="pop_content"> 浮層內(nèi)容 … </div> </div> |
主要js代碼(popmanager.js)
function getWindowScrollTop(win){ var scrollTop=0; if(win.document.documentElement&&win.document.documentElement.scrollTop){ scrollTop=win.document.documentElement.scrollTop; }else if(win.document.body){ scrollTop=win.document.body.scrollTop; } return scrollTop; } function setWindowScrollTop(win, topHeight) { if(win.document.documentElement) { win.document.documentElement.scrollTop = topHeight; } if(win.document.body){ win.document.body.scrollTop = topHeight; } } function getWindowScrollLeft(win){ var scrollLeft=0; if(win.document.documentElement&&win.document.documentElement.scrollLeft){ scrollLeft=win.document.documentElement.scrollLeft; } else if(win.document.body){ scrollLeft=win.document.body.scrollLeft; } return scrollLeft; } function getWindowHeight(win){ var clientHeight=0; if(win.document.body.clientHeight&&win.document.documentElement.clientHeight){ clientHeight = (win.document.body.clientHeight<win.document.documentElement.clientHeight)? win.document.body.clientHeight:win.document.documentElement.clientHeight; }else{ clientHeight = (win.document.body.clientHeight>win.document.documentElement.clientHeight)? win.document.body.clientHeight:win.document.documentElement.clientHeight; } return clientHeight; } function getWindowWidth(win){ var clientWidth=0; if(win.document.body.clientWidth&&win.document.documentElement.clientWidth){ clientWidth = (win.document.body.clientWidth<win.document.documentElement.clientWidth)? win.document.body.clientWidth:win.document.documentElement.clientWidth; }else{ clientWidth = (win.document.body.clientWidth>win.document.documentElement.clientWidth)? win.document.body.clientWidth:win.document.documentElement.clientWidth; } return clientWidth; }
function unpop() { try{ var win = (top && top!=self)?top:window; } catch(e) { return ; }
win.document.getElementById('mask').style.display = "none"; win.document.getElementById("id_popdiv").style.display = "none"; win.document.getElementById("id_iframe_pop").setAttribute('src', '');
}
function pop(url,width,height) { try{ var win = (top && top!=self)?top:window; } catch(e) { return ; }
var topWindowHeight = getWindowHeight(win); var topWindowWidth = getWindowWidth(win);
var lvTop=parseInt((topWindowHeight-height)/2)+parseInt(getWindowScrollTop(win)); var lvLeft=parseInt((topWindowWidth-width)/2)+parseInt(getWindowScrollLeft(win)); lvTop = lvTop<=0?1:lvTop; lvLeft = lvLeft<=0?1:lvLeft;
win.document.getElementById("id_popdiv").style.top=lvTop+"px"; win.document.getElementById("id_popdiv").style.left=lvLeft+"px"; win.document.getElementById("id_popdiv").style.margin="0";
win.document.getElementById("id_iframe_pop").setAttribute('src', url);
win.document.getElementById("id_iframe_pop").setAttribute('width', width); win.document.getElementById("id_iframe_pop").setAttribute('height', height);
win.document.getElementById('mask').style.display = "block"; win.document.getElementById("id_popdiv").style.display = "block"; } |
附:
應(yīng)用較多的div浮層技術(shù)為基于jquery的blockUI技術(shù)。詳情請(qǐng)參考相關(guān)文檔。
posted @ 2012-07-11 21:47 小胡子 閱讀(830) | 評(píng)論 (0) | 編輯 收藏
軟件測(cè)試要求開(kāi)發(fā)人員避免測(cè)試自己開(kāi)發(fā)的程序。從心理學(xué)角度講,這是很有道理的。特別是一個(gè)相對(duì)復(fù)雜的系統(tǒng),開(kāi)發(fā)人員在剛剛開(kāi)發(fā)完成的時(shí)候,尚沉浸于對(duì)自己設(shè)計(jì)的回味之中。此時(shí)去測(cè)試的話(huà)往往會(huì)側(cè)重于程序本身的功能通過(guò)性測(cè)試。很難發(fā)現(xiàn)錯(cuò)誤。
測(cè)試是為發(fā)現(xiàn)錯(cuò)誤而執(zhí)行程序的錯(cuò)誤。一個(gè)人發(fā)現(xiàn)別人身上的不足很容易,但發(fā)現(xiàn)自己身上的錯(cuò)誤便不那么容易了。所謂“吾能指人之失而不能見(jiàn)己之失,吾能指 人之小失而不能見(jiàn)己之大失”者是也。一個(gè)軟件開(kāi)發(fā)人員需要養(yǎng)成一種習(xí)慣,正視自己開(kāi)發(fā)的軟件,特別是剛剛完成的軟件。要看到它的不足,知道他能做什么,不 能做什么。在不能做的時(shí)候是如何處理的。對(duì)邊界條件是否做了嚴(yán)格的判斷及約束。其實(shí)大道相通,有沒(méi)有這樣的意識(shí)往往跟一個(gè)人為人處世的心態(tài),對(duì)自己的認(rèn)知 有密切的聯(lián)系。一個(gè)追求完美,經(jīng)常反思自己的人往往有一種虛懷若谷的情懷,“戰(zhàn)戰(zhàn)兢兢、如履薄冰、如臨深淵”者是也。所謂的方法、套路僅僅是方便于那些不 怎么思考,只會(huì)人云亦云、亦步亦趨的人設(shè)計(jì)的。
說(shuō)了一些心態(tài)方面的,再來(lái)從方法學(xué)上說(shuō)一下軟件測(cè)試的要點(diǎn),對(duì)開(kāi)發(fā)人員來(lái)說(shuō),白盒測(cè)試要比黑盒測(cè)試更重要,這決定著你的系統(tǒng)上線(xiàn)后你能不能安心的睡覺(jué); 測(cè)試的階段主要在單元測(cè)試與集成測(cè)試方面。
先看一張測(cè)試相關(guān)的圖:
按照測(cè)試的過(guò)程,分為單元測(cè)試、集成測(cè)試、系統(tǒng)測(cè)試、驗(yàn)收測(cè)試四點(diǎn)。對(duì)于開(kāi)發(fā)人員來(lái)說(shuō),我只強(qiáng)調(diào)前面兩點(diǎn)。
單元測(cè)試主要測(cè)試編寫(xiě)的類(lèi)、類(lèi)中的函數(shù)等。這是測(cè)試的最小單元。常用的測(cè)試工具有java的JUnit、BoostTest等。
集成測(cè)試側(cè)重于系統(tǒng)的整體功能性測(cè)試,這需要模擬各種可能的請(qǐng)求情況,特別是邊界條件。在多系統(tǒng)中,單個(gè)系統(tǒng)的測(cè)試完了后還需要各個(gè)系統(tǒng)之間的聯(lián)調(diào)。
測(cè)試目標(biāo)方面,除了一般的功能測(cè)試之外還需要穩(wěn)定性測(cè)試、性能測(cè)試、可靠性測(cè)試、適用性測(cè)試、易用性測(cè)試、安全性測(cè)試。
下面詳細(xì)介紹一下各個(gè)測(cè)試要點(diǎn)的測(cè)試內(nèi)容。
1. 功能性測(cè)試:
在軟件測(cè)試領(lǐng)域的通用理解是:“功能測(cè)試是基于產(chǎn)品功能說(shuō)明書(shū),是在已知產(chǎn)品所應(yīng)具有的功能,從用戶(hù)角度來(lái)進(jìn)行功能驗(yàn)證,以確認(rèn)每個(gè)功能是否都能正常使 用、是否實(shí)現(xiàn)了產(chǎn)品規(guī)格說(shuō)明書(shū)的要求、是否能適當(dāng)?shù)亟邮蛰斎霐?shù)鋸而產(chǎn)生正確的輸出結(jié)果等。功能測(cè)試,包括用戶(hù)界面測(cè)試、各種操作的測(cè)試、不同的數(shù)據(jù)輸入、 邏輯思路、數(shù)據(jù)輸出和存儲(chǔ)等的測(cè)試。” 對(duì)于開(kāi)發(fā)人員來(lái)說(shuō),功能性測(cè)試主要是對(duì)系統(tǒng)所支持的功能點(diǎn)的測(cè)試,對(duì)數(shù)據(jù)的測(cè)試,包括數(shù)據(jù)邊界的測(cè)試、數(shù)據(jù)包長(zhǎng)度限制的測(cè)試、各個(gè)功能模塊數(shù)據(jù)的正確性測(cè) 試以及容錯(cuò)處理。該項(xiàng)測(cè)試要先把系統(tǒng)的功能點(diǎn)全部列出來(lái),對(duì)異常數(shù)據(jù)的處理也算在內(nèi)。
2. 穩(wěn)定性測(cè)試:
測(cè)試在壓力情況下內(nèi)存使用情況,cpu使用情況,是否有內(nèi)存泄露,各個(gè)模塊功能是否正常,能否正常提供服務(wù),是否會(huì)在高壓情況下卡死等。這也需要用各種可 能數(shù)據(jù)進(jìn)行壓力測(cè)試,要測(cè)試邊界條件下,收到攻擊時(shí)還能否正常工作,系統(tǒng)有沒(méi)有自清理功能等。個(gè)人的理解,該項(xiàng)測(cè)試類(lèi)似于可靠性測(cè)試,要點(diǎn)在于測(cè)試系統(tǒng)在 極端情況下是否正常。比如一個(gè)房子在高強(qiáng)度地震下的抗震能力。
3. 性能測(cè)試:
測(cè)試程序(一般是服務(wù)器),每秒能正常處理的請(qǐng)求數(shù)。這項(xiàng)測(cè)試一般是尋找系統(tǒng)的性能瓶頸,看看能否滿(mǎn)足實(shí)際的需求。當(dāng)下,一個(gè)服務(wù)器每天處理5000萬(wàn)的請(qǐng)求便可以了。當(dāng)然,通過(guò)優(yōu)化對(duì)性能的追求是沒(méi)有止境的。
4. 安全性測(cè)試:
安全性測(cè)試的主要目的是 確保軟件不會(huì)去完成沒(méi)有預(yù)先設(shè)計(jì)的功能。這一點(diǎn)很重要,需要開(kāi)發(fā)人員在開(kāi)發(fā)時(shí)對(duì)可能出現(xiàn)的情況作細(xì)致的判斷。常見(jiàn)的安全性測(cè)試內(nèi)容有:畸形的文件結(jié)構(gòu)、畸 形的數(shù)據(jù)包、用戶(hù)輸入的驗(yàn)證、驗(yàn)證資源之間的依賴(lài)關(guān)系、配置文件等的格式等。因?yàn)殚_(kāi)發(fā)人員常常假定他所獲取的資源內(nèi)容是符合一定標(biāo)準(zhǔn)或規(guī)則的。附錄十常見(jiàn) 的web安全性測(cè)試的內(nèi)容。
5. 適用性測(cè)試:
附: WEB安全性測(cè)試
一個(gè)完整的WEB安全性測(cè)試可以從部署與基礎(chǔ)結(jié)構(gòu)、輸入驗(yàn)證、身份驗(yàn)證、授權(quán)、配置管理、敏感數(shù)據(jù)、會(huì)話(huà)管理、加密。參數(shù)操作、異常管理、審核和日志記錄等幾個(gè)方面入手。
1. 安全體系測(cè)試
1) 部署與基礎(chǔ)結(jié)構(gòu)
l 網(wǎng)絡(luò)是否提供了安全的通信
l 部署拓?fù)浣Y(jié)構(gòu)是否包括內(nèi)部的防火墻
l 部署拓?fù)浣Y(jié)構(gòu)中是否包括遠(yuǎn)程應(yīng)用程序服務(wù)器
l 基礎(chǔ)結(jié)構(gòu)安全性需求的限制是什么
l 目標(biāo)環(huán)境支持怎樣的信任級(jí)別
2) 輸入驗(yàn)證
l 如何驗(yàn)證輸入
A. 是否清楚入口點(diǎn)
B. 是否清楚信任邊界
C. 是否驗(yàn)證Web頁(yè)輸入
D. 是否對(duì)傳遞到組件或Web服務(wù)的參數(shù)進(jìn)行驗(yàn)證
E. 是否驗(yàn)證從數(shù)據(jù)庫(kù)中檢索的數(shù)據(jù)
F. 是否將方法集中起來(lái)
G. 是否依賴(lài)客戶(hù)端的驗(yàn)證
H. 應(yīng)用程序是否易受SQL注入攻擊
I. 應(yīng)用程序是否易受XSS攻擊
l 如何處理輸入
3) 身份驗(yàn)證
l 是否區(qū)分公共訪(fǎng)問(wèn)和受限訪(fǎng)問(wèn)
l 是否明確服務(wù)帳戶(hù)要求
l 如何驗(yàn)證調(diào)用者身份
l 如何驗(yàn)證數(shù)據(jù)庫(kù)的身份
l 是否強(qiáng)制試用帳戶(hù)管理措施
4) 授權(quán)
l 如何向最終用戶(hù)授權(quán)
l 如何在數(shù)據(jù)庫(kù)中授權(quán)應(yīng)用程序
l 如何將訪(fǎng)問(wèn)限定于系統(tǒng)級(jí)資源
5) 配置管理
l 是否支持遠(yuǎn)程管理
l 是否保證配置存儲(chǔ)的安全
l 是否隔離管理員特權(quán)
6) 敏感數(shù)據(jù)
l 是否存儲(chǔ)機(jī)密信息
l 如何存儲(chǔ)敏感數(shù)據(jù)
l 是否在網(wǎng)絡(luò)中傳遞敏感數(shù)據(jù)
l 是否記錄敏感數(shù)據(jù)
7) 會(huì)話(huà)管理
l 如何交換會(huì)話(huà)標(biāo)識(shí)符
l 是否限制會(huì)話(huà)生存期
l 如何確保會(huì)話(huà)存儲(chǔ)狀態(tài)的安全
8) 加密
l 為何使用特定的算法
l 如何確保加密密鑰的安全性
9) 參數(shù)操作
l 是否驗(yàn)證所有的輸入?yún)?shù)
l 是否在參數(shù)過(guò)程中傳遞敏感數(shù)據(jù)
l 是否為了安全問(wèn)題而使用HTTP頭數(shù)據(jù)
10) 異常管理
l 是否使用結(jié)構(gòu)化的異常處理
l 是否向客戶(hù)端公開(kāi)了太多的信息
11) 審核和日志記錄
l 是否明確了要審核的活動(dòng)
l 是否考慮如何流動(dòng)原始調(diào)用這身份
2. 應(yīng)用及傳輸安全
WEB應(yīng)用系統(tǒng)的安全性從使用角度可以分為應(yīng)用級(jí)的安全與傳輸級(jí)的安全,安全性測(cè)試也可以從這兩方面入手。
應(yīng)用級(jí)的安全測(cè)試的主要目的是查找Web系統(tǒng)自身程序設(shè)計(jì)中存在的安全隱患,主要測(cè)試區(qū)域如下。
l 注冊(cè)與登陸:現(xiàn)在的Web應(yīng)用系統(tǒng)基本采用先注冊(cè),后登錄的方式。
A. 必須測(cè)試有效和無(wú)效的用戶(hù)名和密碼
B. 要注意是否存在大小寫(xiě)敏感,
C. 可以嘗試多少次的限制
D. 是否可以不登錄而直接瀏覽某個(gè)頁(yè)面等。
l 在線(xiàn)超時(shí):Web應(yīng)用系統(tǒng)是否有超時(shí)的限制,也就是說(shuō),用戶(hù)登陸一定時(shí)間內(nèi)(例如15分鐘)沒(méi)有點(diǎn)擊任何頁(yè)面,是否需要重新登陸才能正常使用。
l 操作留痕:為了保證Web應(yīng)用系統(tǒng)的安全性,日志文件是至關(guān)重要的。需要測(cè)試相關(guān)信息是否寫(xiě)進(jìn)入了日志文件,是否可追蹤。
l 備份與恢復(fù):為了防范系統(tǒng)的意外崩潰造成的數(shù)據(jù)丟失,備份與恢復(fù)手段是一個(gè)Web系統(tǒng)的必備功能。備份與恢復(fù)根據(jù)Web系統(tǒng)對(duì)安全性的要求可以采用多種手 段,如數(shù)據(jù)庫(kù)增量備份、數(shù)據(jù)庫(kù)完全備份、系統(tǒng)完全備份等。出于更高的安全性要求,某些實(shí)時(shí)系統(tǒng)經(jīng)常會(huì)采用雙機(jī)熱備或多級(jí)熱備。除了對(duì)于這些備份與恢復(fù)方式 進(jìn)行驗(yàn)證測(cè)試以外,還要評(píng)估這種備份與恢復(fù)方式是否滿(mǎn)足Web系統(tǒng)的安全性需求。
傳輸級(jí)的安全測(cè)試是考慮到Web系統(tǒng)的傳輸?shù)奶厥庑?,重點(diǎn)測(cè)試數(shù)據(jù)經(jīng)客戶(hù)端傳送到服務(wù)器端可能存在的安全漏洞,以及服務(wù)器防范非法訪(fǎng)問(wèn)的能力。一般測(cè)試項(xiàng)目包括以下幾個(gè)方面。
l HTTPS和SSL測(cè)試:默認(rèn)的情況下,安全HTTP(Soure HTTP)通過(guò)安全套接字SSL(Source Socket Layer)協(xié)議在端口443上使用普通的HTTP。HTTPS使用的公共密鑰的加密長(zhǎng)度決定的HTTPS的安全級(jí)別,但從某種意義上來(lái)說(shuō),安全性的保證 是以損失性能為代價(jià)的。除了還要測(cè)試加密是否正確,檢查信息的完整性和確認(rèn)HTTPS的安全級(jí)別外,還要注意在此安全級(jí)別下,其性能是否達(dá)到要求。
l 服務(wù)器端的腳本漏洞檢查:存在于服務(wù)器端的腳本常常構(gòu)成安全漏洞,這些漏洞又往往被黑客利用。所以,還要測(cè)試沒(méi)有經(jīng)過(guò)授權(quán),就不能在服務(wù)器端放置和編輯腳本的問(wèn)題。
l 防火墻測(cè)試:防火墻是一種主要用于防護(hù)非法訪(fǎng)問(wèn)的路由器,在Web系統(tǒng)中是很常用的一種安全系統(tǒng)。防火墻測(cè)試是一個(gè)很大很專(zhuān)業(yè)的課題。這里所涉及的只是對(duì)防火墻功能、設(shè)置進(jìn)行測(cè)試,以判斷本W(wǎng)eb系統(tǒng)的安全需求。
posted @ 2012-07-11 21:45 小胡子 閱讀(156) | 評(píng)論 (0) | 編輯 收藏
最近在給客戶(hù)做項(xiàng)目的時(shí)候客戶(hù)提出要求要給圖片加水印, 在添加文字水印時(shí),讓用戶(hù)自定義自體,當(dāng)用戶(hù)在選擇字體時(shí),如果勾選了刪除線(xiàn)和下劃線(xiàn)選項(xiàng),而java.awt.Font不支持下劃線(xiàn)和刪除線(xiàn)的style, 這怎么辦呢?
還好,Java提供了 AttributedString 類(lèi), 通過(guò)
attributedString.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
attributedString.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
即可給文字添加刪除線(xiàn)和下劃線(xiàn),再通過(guò)
graphics2D.drawString(as.getIterator(), x, y);
就可以顯示下劃線(xiàn)和刪除線(xiàn)的效果了。
- import java.awt.Font;
- import java.awt.Graphics;
- import java.awt.Graphics2D;
- import java.awt.RenderingHints;
- import java.awt.font.TextAttribute;
- import java.text.AttributedString;
- import javax.swing.JFrame;
- import javax.swing.JPanel;
- public class IteratorUnderStrike extends JPanel{
- public void paint(Graphics g) {
- Graphics2D g2 = (Graphics2D) g;
- String s = "/"www.java2s.com/" is great.";
- g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
- RenderingHints.VALUE_ANTIALIAS_ON);
- Font plainFont = new Font("Times New Roman", Font.PLAIN, 24);
- AttributedString as = new AttributedString(s);
- as.addAttribute(TextAttribute.FONT, plainFont);
- as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, 1, 15);
- as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, 18, 25);
- g2.drawString(as.getIterator(), 24, 70);
- }
- public static void main(String[] args) {
- JFrame f = new JFrame();
- f.getContentPane().add(new IteratorUnderStrike());
- f.setSize(850, 250);
- f.show();
- }
- }
http://yanghuidang.iteye.com/blog/1203582
posted @ 2012-07-10 13:09 小胡子 閱讀(3671) | 評(píng)論 (1) | 編輯 收藏