純真 IP 資料庫(QQWry.dat)包含約 38 萬筆記錄。需先使用官方查詢工具將其解壓為文字檔,並確保編碼轉為 UTF-8,避免中文亂碼。
考量龐大資料量,採用前端 AJAX 輪詢、後端分批處理的模式,避免單一請求逾時,並能即時顯示進度。
頁面包含資料庫連線設定表單、檔案上傳控制項,以及即時日誌顯示區。核心流程:
iframe 模擬非同步檔案上傳(上傳完成觸發父頁面 insertIP())。/do.ashx?action=init 初始化:建立資料表、快取 IP 清單、回傳總筆數。/do.ashx?action=insert&num=隨機數 分批寫入,每批預設 300 筆,直到隊列歸零。function insertIP() {
log.innerHTML += "開始分析資料…<br />";
j.ajax.post("/do.ashx?args=ImportIPData&action=init", "",
function(x) {
var d = eval(x)[0];
recordCount = d.count;
log.innerHTML += "<font color=green>分析成功: 伺服器 " + d.server + ",共 " + recordCount + " 筆</font><br /><div id='recordLog'></div>";
insert();
},
function(x) { log.innerHTML += "<font color=red>異常終止</font>"; }
);
}
function insert() {
if (!reLog) reLog = document.getElementById("recordLog");
var num = Math.floor(Math.random() * 100);
j.ajax.post("/do.ashx?args=ImportIPData&action=insert", "num=" + num,
function(x) {
var d = eval(x)[0];
reLog.innerHTML = "已寫入 " + (recordCount - d.count) + " 筆,剩餘 " + d.count + " 筆,本批 " + d.insertNum + " 筆";
if (d.count != 0) insert();
else reLog.innerHTML = "恭喜,寫入完畢!";
},
function(x) { alert(x); }
);
}

單一進入點處理三種 action:
HttpRuntime.Cache["ip_data"],並將連線字串、資料表名存入 Session。num 參數決定本批筆數,使用參數化 SQL 批次 INSERT,完成後移除已寫入項目,回傳剩餘筆數。public void ProcessRequest(HttpContext context) {
if (context.Request.RequestType != "POST") return;
string action = context.Request["action"];
if (string.IsNullOrEmpty(action) || action == "submit") {
// 儲存連線字串與資料表名
var sb = new StringBuilder();
sb.Append("server=").Append(context.Request["dbserver"])
.Append(";database=").Append(context.Request["dbname"])
.Append(";uid=").Append(context.Request["dbuid"])
.Append(";pwd=").Append(context.Request["dbpwd"]);
context.Session["ip_dbconnstring"] = sb.ToString();
context.Session["ip_tablename"] = context.Request["tbname"];
// 讀取並快取 IP 資料
var ipList = new List();
using (var sr = new StreamReader(context.Request.Files[0].InputStream, Encoding.UTF8)) {
while (sr.Peek() != -1) {
ipList.Add(Regex.Replace(sr.ReadLine(), @"\s{2,}", " "));
}
}
HttpRuntime.Cache.Insert("ip_data", ipList);
// 回傳初始資訊並觸發前端插入
sb.Clear();
sb.Append("[{server:'").Append(context.Request["dbserver"])
.Append("',count:").Append(ipList.Count)
.Append(",insertNum:0,taskNum:0}]");
context.Session["ip_info"] = sb.ToString();
context.Response.Write("");
}
else {
using (var conn = new SqlConnection(context.Session["ip_dbconnstring"] as string)) {
string tbname = context.Session["ip_tablename"] as string;
if (action == "init") {
var cmd = new SqlCommand($@"IF NOT EXISTS(SELECT * FROM sysobjects WHERE name='{tbname}' AND xtype='u')
CREATE TABLE {tbname}(id BIGINT IDENTITY(1,1) PRIMARY KEY, sip NVARCHAR(15), eip NVARCHAR(15), area NVARCHAR(80), name NVARCHAR(80))", conn);
conn.Open(); cmd.ExecuteNonQuery();
context.Response.Write(context.Session["ip_info"]);
}
else if (action == "insert") {
var ipList = HttpRuntime.Cache["ip_data"] as IList;
int insertNum = int.TryParse(context.Request["num"], out var n) && n > 0 ? n : 300;
var cmd = new SqlCommand { Connection = conn };
cmd.Parameters.AddRange(new[] {
new SqlParameter("@sip", ""), new SqlParameter("@eip", ""),
new SqlParameter("@area", ""), new SqlParameter("@name", "")
});
conn.Open();
for (int i = 0; i < insertNum && i < ipList.Count; i++) {
var arr = ipList[i].Split(' ');
cmd.CommandText = $@"IF NOT EXISTS(SELECT id FROM {tbname} WHERE sip=@sip AND eip=@eip)
INSERT INTO {tbname} VALUES(@sip,@eip,@area,@name)";
cmd.Parameters["@sip"].Value = arr[0];
cmd.Parameters["@eip"].Value = arr[1];
cmd.Parameters["@area"].Value = arr[2];
cmd.Parameters["@name"].Value = arr.Length >= 4 ? arr[3] : "";
cmd.ExecuteNonQuery();
ipList.RemoveAt(i--);
}
var sb = new StringBuilder();
sb.Append("[{count:").Append(ipList.Count)
.Append(",insertNum:").Append(insertNum).Append("}]");
context.Response.Write(sb.ToString());
}
}
}
}
38 萬筆資料約 1 小時完成匯入(視資料庫效能而定)。完成後資料表截圖:

IF NOT EXISTS 避免重複 IP 區間寫入。