之前給公司做打印都是用ActiveX控件,只支持IE瀏覽器,最近需要支持谷歌,又不想去學谷歌插件編寫,於是就用本地啟動一個http服務器來供瀏覽器調用(寫成windows服務更好),同事用了都說好(笑)!為了方便大家使用,重新簡單的封裝了一下,源碼下載:點我下載
源碼簡單的封裝了一個webserver(基於httplistener,可以使用Nancy框架代替),可以大致了解HTTP服務器處理的流程:APACHE,NINGX等服務器主要負責響應瀏覽器HTTP(基於SOCKET)的請求,並將請求轉交給JAVA,C#,PHP等語言處理(應該就是啟動了一個該語言的虛擬機解析執行對應的代碼),再將處理結果返回給瀏覽器
1 namespace LocalPrint.Web 2 { 3 //本地微型HTTP服務器,可以理解網絡請求處理的服務器處理流程 4 class WebServer 5 { 6 HttpListener httpListener; 7 public string Err; 8 Dictionary<string, IHttpHandler> handlerMap = new Dictionary<string, IHttpHandler>(); 9 //啟動本地HTTP服務器,相當於APACHE,NGINX之類的WEB服務器,接受瀏覽器發送過來的SOCKET請求 10 public bool RunWeb(string url) 11 { 12 try 13 { 14 httpListener = new HttpListener(); 15 httpListener.Prefixes.Add(url); 16 httpListener.Start(); 17 var th = new Thread(Process); 18 th.IsBackground = true; 19 th.Start(); 20 return true; 21 } 22 catch (Exception ex) 23 { 24 Err = "啟動本地服務器出現問題:" + ex.Message; 25 return false; 26 } 27 } 28 //設置相應路由和相應的處理類 29 public bool AddHandler(string url,IHttpHandler httpHandler) 30 { 31 try 32 { 33 handlerMap.Add(url, httpHandler); 34 return true; 35 } 36 catch (Exception ex) 37 { 38 Err = "添加路徑出錯:" + ex.Message; 39 return false; 40 } 41 } 42 //HTTP數據處理,這一部分相當於C#,JAVA和PHP和其他語言的功能,WEB服務器將請求轉發給相應的語言處理,並把結果返回給瀏覽器 43 void Process() 44 { 45 for (; ; ) 46 { 47 var cnx = httpListener.GetContext();//獲取瀏覽器請求上下文,串行處理,也可以改成並行 48 var req = cnx.Request; 49 var rep = cnx.Response; 50 rep.ContentEncoding = Encoding.UTF8; 51 rep.Headers.Add("Access-Control-Allow-Origin", "*");//允許瀏覽器跨域!非常重要 52 rep.StatusCode = 404; 53 var ret = "pag not found"; 54 //rep.ContentType = "text";//返回內容,這里為text,ajax里面的請求datatype也需要設置為text或者html,不然會為null 55 //這一部分其實就是大部分MVC網絡框架里的路由部分!這里簡單的發送原始文本給handler處理 56 foreach (var kv in handlerMap) 57 { 58 if(System.Text.RegularExpressions.Regex.IsMatch(req.RawUrl,kv.Key))//正則匹配 59 { 60 var data = ""; 61 if (req.HttpMethod == "GET") 62 data = req.RawUrl;//不做任何處理,直接將原始的http請求轉發到handler。。。 63 else 64 using (var r = new StreamReader(req.InputStream, Encoding.UTF8)) 65 { 66 data = r.ReadToEnd(); 67 } 68 ret = kv.Value.Handler(data); 69 rep.StatusCode = 200;//ok 70 break; 71 } 72 } 73 //返回處理結果給瀏覽器 74 using (var w = new StreamWriter(rep.OutputStream, Encoding.UTF8)) 75 { 76 w.WriteLine(ret); 77 } 78 } 79 } 80 81 } 82 }
1 void Process() 2 { 3 for(; ; ) 4 { 5 var cnx = httpListener.GetContext(); 6 var req = cnx.Request; 7 var rep = cnx.Response; 8 rep.Headers.Add("Access-Control-Allow-Origin", "*"); 9 var txt = ""; 10 if (req.HttpMethod == "POST") 11 { 12 var fp = ""; 13 using (var r = new StreamReader(req.InputStream, Encoding.UTF8)) 14 { 15 fp = r.ReadToEnd(); 16 } 17 var printer = req.QueryString["printer"]; 18 switch (req.Url.LocalPath) 19 { 20 case "/printZPP": 21 printComp1.Print(fp, printer); 22 break; 23 case "/printTJBB": 24 printComp1.PrintBB(fp, printer); 25 break; 26 case "/printQD": 27 printComp1.PrintQd(fp, printer); 28 break; 29 case "/printAll": 30 printComp1.UniversalPrint(fp, printer); 31 break; 32 default: 33 //rep.StatusCode = 404; 34 txt = "404"; 35 break; 36 } 37 } 38 else 39 { 40 switch (req.Url.LocalPath) 41 { 42 case "/GetPrintNames": 43 txt = printComp1.GetPrinterNames(); 44 break; 45 case "/GetPrinter": 46 { 47 var id = req.QueryString["id"]; 48 txt = printComp1.GetLocalPrinter(id); 49 } 50 break; 51 case "/SetPrinter": 52 { 53 var id = req.QueryString["id"]; 54 var printer = req.QueryString["printer"]; 55 printComp1.SetLocalPrinter(id, printer); 56 } 57 break; 58 default: 59 txt = printComp1.GetPrinterNames(); 60 //rep.StatusCode = 404; 61 txt = "404"; 62 break; 63 } 64 } 65 66 using (var w = new StreamWriter(rep.OutputStream, Encoding.UTF8)) 67 { 68 w.WriteLine(txt); 69 } 70 } 71 }
HTTP處理接口,PrintHandler派生此接口處理打印任務,也可以派生接口執行其他本地任務,只是簡單的處理get和post文本,沒有具體的參數解析,需要類自行解析。。。
1 //類似於MVC框架的ACTION或者CONTROLER,可派生此接口實現其他操作 2 public interface IHttpHandler 3 { 4 string Handler(string txt);//簡單的處理文本消息 5 }
IPrint打印接口,派生此接口執行具體打印任務,打印格式這塊可以封裝出一整套的處理代碼(常用的標簽,表格打印和格式設置),有空再處理了
1 //打印接口,需要打印格式,派生此接口即可 2 public interface IPrint 3 { 4 bool Print(Graphics g);//返回值為是否還有打印需要打印的頁內容,即是否打印結束 5 }
啟動本地服務器后,瀏覽器直接訪問或ajax跨域訪問本地服務器即可調用本地處理服務!
1 $.ajax({ 2 url : "http://localhost:23333/print", 3 type : "POST", 4 contentType: "application/json;charset=utf-8", 5 data : "{'name':'大蘿卜卜','age':32,'sex':true}", 6 dataType : "text", 7 success : function(result) { 8 if (result == "ok") { 9 alert("打印成功!"); 10 } else { 11 alert("打印出錯:"+result ) 12 } 13 }, 14 error:function(msg){ 15 aler('Error:'+msg); 16 } 17 })
本地測試
var web = new WebClient(); var txt = textBox1.Text; byte[] bytearray = Encoding.UTF8.GetBytes(txt); txt = Convert.ToBase64String(bytearray); txt = System.Web.HttpUtility.UrlEncode(txt, Encoding.UTF8);//必須經過url編碼 web.Encoding = Encoding.UTF8; web.Headers.Add("ContentType", "application/x-www-form-urlencoded"); try { var ret = web.UploadString("http://127.0.0.1:1234", "POST", "args=" + txt); textBox2.Text = Encoding.UTF8.GetString(Convert.FromBase64String(ret)); } catch (WebException ex) { var rsp = ex.Response as HttpWebResponse; if(rsp !=null) { var code = (int)rsp.StatusCode; textBox2.Text = code + "\r\n"; var stream = rsp.GetResponseStream(); using (var s = new StreamReader(stream)) { textBox2.Text += s.ReadToEnd(); } } else { textBox2.Text = ex.Message; } }
為了發表博客,花了2個小時,把代碼整理,簡單的封裝成接口,平常寫代碼哪有這么費事,分分鍾寫完交差了事(意大利面條,笑),寫文章不易,點個贊吧。
