自從搬到 BlogEngine.NET 之後,每天晚上都沒閒著,一點一點的慢慢把整個網站改成我要的樣子... 資料庫 / 檔案是我最在意的,當然要優先顧好,光這部份就花了一個多禮拜...
最早找到了 CS2007 --> BlogML --> BlogEngine,大概把我想要的 90% 資料都轉過來了,不過看到那 10% 的資訊沒過來,心裡就很不是滋味...
"這是我要一直保留下來的資料耶,現在沒把資料補回來,以後就永遠補不回來了..."
就是心裡一直這樣想,所以... 重匯吧! 不然以後後悔也是補不回來的。首先當然是想先從 BlogEngine 匯入 BlogML 的工具下手,看了一下,沒提供 Source Code ? 再看一下,一篇文章一個 Web Service Call,一則回應也是一個 Web Service Call ... 感覺起來有點沒效率,不過不管了,我內容也不多 (兩百多篇文章) ... 既然有 Web Services,先看看它的 WSDL ...
http://columns.chicken-house.net/api/blogImporter.asmx
沒幾個 WebMethod 嘛,不過看一看它的 Interface 就已經漏掉很多資訊沒轉進來了 (像是我要的原 CS PostID,事後作新舊網址對照表用,還有 PageViewCount... etc),要改原程式也是個大工程,不但 CLIENT 要改,WEB METHOD 也要擴充... 我又不是非得遠端用 WEB SERVICES 執行匯入不可,就當下決定另外寫一個匯入程式還比較快...。重寫的話就得研究它的寫法,正好每個 WebMethod 都只作單一的動作,裡面的實作就是現成的範例... 省了不少熟悉 API 的時間 :P
事後證明這作法沒錯,BlogEngine 的 LIB 蠻簡單易懂的,另外寫真的比較快... 就一切以原 .ASMX 的程式碼為藍本,寫了一個 .ASHX ,不作什麼事,就是把事先放在 ~/App_Data 下的 BLOGML 讀進來,一篇一篇 POST 處理,一個一個回應處理... 兩層迴圈就搞定原本那 90% 的匯入作業...
接下來就是自訂的部份了。先來列一下那些是我要補的資訊:
- 每篇 POST 的 COUNTER
- 每則回應的詳細資訊
- 每篇 POST 的 ID ( CS2007 內的 ID ),及匯入後的新 ID
- 所有必要的網址修正 (圖檔,站內文章連結..)
- 原程式的一些缺陷修正 (如前文提到,Modified Date錯誤的問題)
源頭就有東西要補了。如果一切基於 BlogML 的話,BlogML 沒定義到的資料就沒機會撈出來了。看了一下我還需要額外抓出來的資訊,像匿名的 COMMENTS 作者資訊,IP 等... 不過這些都用某種序列化的方式存在 CSDB,ㄨ!! 真麻煩... 在 CS2007 DB 內的 [cs_posts] 有這兩個欄位: PropertyNames, PropertyValues... 沒錯,全部壓成一個大字串,得自己寫程式去 PARSE ...
CS2007 的作法是這樣,在 PropertyNames 欄位放的值長的像這樣 (包含 Name, 型別, 字串起始及結束位置):
SubmittedUserName:S:0:3:TitleUrl:S:3:35:
而在 PropertyValues 對應的值為:
小熊子http://michadel.dyndns.org/netblog/
拆起來有點小麻煩,PropertyNames 是 {Name}:{Type}:{StartPos}:{EndPos}: 的組合,有多組的話就一直重複下去。以 Comments 的 SubmittedUserName 來說,它的型別是 S ( String ),它的值就要從後面的 PropertyValues 整個字串的第 0 個字元 ~ 第 3 個字元之間的值 ( 小熊子 ),而 TitleUrl 則是第 3 個字元 ~ 第 35 個字元的值 ( http://michadel.dyndns.org/netblog/ ) ...
看來用 T-SQL 拆或是用 XPath / XSLT 拆都有點辛苦,想想算了... 這段 CODE 跑不掉一定要寫... 就硬解出來吧。解出來後就可以修正 CS2007 匿名張貼的 Comment 沒正確顯示在 BlogML 內的問題。
接下來要把 SQL 這堆資料倒成 XML 檔,偷吃步一下,找工具直接倒出來就好。反正只作一次,可以不要寫 CODE 就不要寫了... SQL2005 要把 QUERY 結果存成 XML 倒是很簡單,打開 SQL2005 Management Studio, 執行這段 QUERY:
1: select
2: PostID,
3: PostAuthor,
4: PropertyNames,
5: PropertyValues
6: from cs_posts
7: where ApplicationPostType = 4
8: for xml auto
會看到這樣的畫面:
點下去就是 XML 了,手到加上頭尾的 Root Element 存檔就搞定了。接下來補段 CODE 把我要的 Property 拆出來存 XML 檔,第一步驟收工!
接下來這堆問題就以 (4) 最麻煩了。原本想的很天真,用文字編輯器把所有 XML 檔替換掉就沒事... 錯! 先從最單純的圖檔路逕來看吧..
[替換圖檔及下載檔案的網址]
最基本的就是圖檔網址... 在 CS2007 裡圖檔是這樣放的 (WLW會幫你補上絕對路逕,不是用相對路逕)
http://columns.chicken-house.net/blogs/chicken/xxxxxxx/xxx.jpg
而看了 BlogEngine 的作法,它用 HttpHandler 的方式來提供前端下載圖檔,格式像這樣:
http://columns.chicken-house.net/xxxxx/wp-content/be-files/xxx.jpg
而 BlogEngine 的設定檔是這樣寫的:
1: <httpHandlers>
2: <add verb="*"
3: path="file.axd"
4: type="BlogEngine.Core.Web.HttpHandlers.FileHandler, BlogEngine.Core"
5: validate="false"/>
6: <add verb="*"
7: path="image.axd"
8: type="BlogEngine.Core.Web.HttpHandlers.ImageHandler,
9: BlogEngine.Core"
10: validate="false"/>
11: .....
12: </httpHandlers>
看起來不難,理論上我只要找到原網址,把 ~/blog/chicken/ 後的路逕切出來,前段改成 image.axd?picthre= 就可以了。
大致上是這樣沒錯,不過變數還不少... 因為有好幾種網址 @_@
- 分家前 ( http://community.chicken-house.net/blogs/chicken/xxxxx )
- 分家後 ( http://columns.chicken-house.net/blogs/chicken/xxxxx )
- 古早時代手工打的 ( /blogs/chicken/xxxxx )
- 有些內建的要避開,像 ( http://columns.chicken-house.net/blogs/chicken/rss.aspx )
代換要嘛一次做完,要嘛一定要照 1 2 3 順序,否則先做 (3) 就會把 (1) (2) 都破壞掉了... 但是怎麼想程式都很不乾脆,雜七雜八的 CODE 一堆... 最後搬出 http://regexlib.com/ 這個網站,它提供大量的 Regular Expression 的資料庫供你使用,也提供了線上版本讓你測試 MATCH 的結果,很好用! 一定要推一下...
最後定案的 Regular Expression 長這樣:
(http\://(columns|community)\.chicken\-house\.net)?/blogs/chicken/([a-zA-Z0-9\-_\./%]*)
一切都很順利,簡單的就精確的抓到要代換的範圍。補一下小插曲,最後轉完才發現漏掉 (4),不過沒幾篇,就手動改掉了 :P,所以這段 REGEXP 是沒處理到 (4) 這種情況的...
[站內連結的修正]
再來就開始麻煩了... 動手前我先想了一下,每篇文章要匯進去後才會知道它的新 ID,新網址... 在還不知道新網址之前有些內容的聯結就無法替換了,怎麼樣都不可能在 1 PASS 內解決這問題... 只能用 2 PASS 來處理了。
既然這樣,第一次匯入就不修站內聯結了,只要把新舊 ID 記下來就好...
頭痛的來了,CS2007 的網址格式有好幾種 @_@ (難怪它有專門一個 config 檔來設定幾十個 URL Rewrite 的設定...):
- /blogs/chicken/archive/2008/06/20/1234.aspx
- /blogs/chicken/archive/2008/06/20/MyPostTitleHash.aspx
- /blogs/1234.aspx
其中 1234 是 CS2007 內部使用的 PostID . 再看看 BlogEngine 的網址格式:
- /post.aspx?id=xxxx-xxxx-xxxxxx-xxxxxxxxxxx
- /post/xxxxxxxxxxxxxxxx.aspx (某種 TITLE 的 HASH,它稱作 SLUG)
還好 BlogEngine 單純多了,只要抓到 (1) 的 ID,SLUG 就有 API 抓的到...
記得 CS 網址還有不需要 URL Rewrite 的版本 ( /blogs/xxxxx.aspx?postID=1234 這樣),不過從來沒出現在我文章內容,就不理它了...
這裡的重點不是單純的代換了,還要精確的抓出 1234 這段 PostID .. 最後定案的 Regular Expression 長這樣:
(http\://(columns|community)\.chicken\-house\.net)?/blogs/chicken/archive/\d+/\d+/\d+/(\d+)\.aspx
果然人家說 Regular Expression 是 "WRITE ONLY LANGUAGE" 真有道理,寫完連我自己都看不懂這堆符號是在幹嘛... 最後新舊 ID 都抓到就可以產生出正確的新站內連結了..
[站外連結修正]
我自己的 LINK 找到就可以修,不過寫在別人那邊的我那修的到啊... 因此就要靠上一篇講的,想辦法要去接有人點到舊的 CS LINK,然後當場做轉址的動作...
好在舊的文章不會再變多了,靠前面做出來的對照表就有足夠的資訊了... 成果看上一篇就知道了。這裡主要是靠 CSUrlMap.cs 這個我自己寫的 HttpHandler 來解決所有連到 /blogs/*.aspx 的連結...
方法差不多,我得從各種可能的網址格式,抓出舊的 CS PostID,然後查表,找出新的網址,把使用者引導到新的頁面... 貼一下主程式的部份:
1: public void ProcessRequest(HttpContext context)
2: {
3: if (matchRss.IsMatch(context.Request.Path) == true)
4: {
5: // redir to RSS
6: context.Response.ContentType = "text/xml";
7: context.Response.TransmitFile("~/blogs/rss.xml");
8: }
9: else if (matchPostID.IsMatch(context.Request.Path) == true)
10: {
11: // redir to post URL
12: Match result = matchPostID.Match(context.Request.Path);
13: if (result != null && result.Groups.Count > 0)
14: {
15: string csPostID = result.Groups[result.Groups.Count - 1].Value;
16: if (this.MAP.postIDs.ContainsKey(csPostID) == true)
17: {
18: context.Items["postID"] = this.MAP.postIDs[csPostID];
19: context.Items["redirDesc"] = "網站系統更新,原文章已經搬移到新的網址。";
20: }
21: else
22: {
23: context.Items["redirDesc"] = "查無此文章代碼,文章不存在或是已被刪除。將返回網站首頁。";
24: }
25: }
26: else
27: {
28: context.Items["redirDesc"] = "4444";
29: }
30: }
31: else if (matchPostURL.IsMatch(context.Request.Path) == true)
32: {
33: context.Items["postID"] = this.MAP.postURLs[context.Request.Path];
34: context.Items["redirDesc"] = "網站系統更新,原文章已經搬移到新的網址。";
35: }
36: else
37: {
38: context.Items["redirDesc"] = "查無此頁。將返回網站首頁。";
39: }
40: context.Server.Transfer("~/blogs/AutoRedirFromCsUrl.aspx");
41: }
看程式說故事,大家都會吧 :D,結果就是上一篇各位看到的樣子... 已經沒力一行一行再說明下去了 :P
寫到這邊真是大工程 @_@,動作都不困難,但是都是雜七雜八的工作,害我搞了一個禮拜... 不過到此為止搬家要處理的資料部份全部都完成了。下一篇就輕鬆多了,把網站的版面調整成我想要的樣式... 有興趣的人請多等一等吧 :D 主題會放在跟 FunP 推推王的密切整合上... 敬請期待 :D