之前给公司做打印都是用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 = ;
  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 = ;//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 = "";
  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 = "";
  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. }
  1. 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. })

本地测试

  1. var web = new WebClient();
  2. var txt = textBox1.Text;
  3. byte[] bytearray = Encoding.UTF8.GetBytes(txt);
  4. txt = Convert.ToBase64String(bytearray);
  5. txt = System.Web.HttpUtility.UrlEncode(txt, Encoding.UTF8);//必须经过url编码
  6. web.Encoding = Encoding.UTF8;
  7. web.Headers.Add("ContentType", "application/x-www-form-urlencoded");
  8.  
  9. try
  10. {
  11. var ret = web.UploadString("http://127.0.0.1:1234", "POST", "args=" + txt);
  12. textBox2.Text = Encoding.UTF8.GetString(Convert.FromBase64String(ret));
  13.  
  14. }
  15. catch (WebException ex)
  16. {
  17. var rsp = ex.Response as HttpWebResponse;
  18. if(rsp !=null)
  19. {
  20. var code = (int)rsp.StatusCode;
  21. textBox2.Text = code + "\r\n";
  22. var stream = rsp.GetResponseStream();
  23. using (var s = new StreamReader(stream))
  24. {
  25. textBox2.Text += s.ReadToEnd();
  26. }
  27. }
  28. else
  29. {
  30. textBox2.Text = ex.Message;
  31.  
  32. }
  33. }

为了发表博客,花了2个小时,把代码整理,简单的封装成接口,平常写代码哪有这么费事,分分钟写完交差了事(意大利面条,笑),写文章不易,点个赞吧。

基于HTTP可供浏览器调用的本地打印程序的更多相关文章

  1. 学习笔记:URL Protocol在浏览器中打开本地应用程序

    看到阿里的网站上可以通过点击卖家的旺旺图标从而调用本地的阿里旺旺程序,而且还可以传递当前浏览者需要咨询的商品.这是怎么实现的呢?是通过URLProtocol来完成. 原理还没有太清楚,即在系统里注册一 ...

  2. chrome浏览器插件启动本地应用程序

    chrome浏览器插件启动本地应用程序 2014-04-20 00:04:30|  分类: 浏览器插件|举报|字号 订阅     下载LOFTER我的照片书  |     chrome的插件开发这里就 ...

  3. 一个实现浏览器网页与本地程序之间进行双向调用的轻量级、强兼容、可扩展的插件开发平台—PluginOK中间件

    通过PluginOK中间件插件平台(原名本网通WebRunLocal)可实现在网页中的JavaScript脚本无障碍访问本地电脑的硬件.调用本地系统的API及相关组件,同时可彻底解决ActiveX组件 ...

  4. ubuntu下浏览器调用本地应用程序

    ubunut下浏览器调用本地应用程序需要desktop文件和scheme协议的支持,和windows 的url protocol类似,只是注册协议的方式不一样. 首先是desktop文件,里面需要加入 ...

  5. mac 下基于firebreath 开发多浏览器支持的浏览器插件

    mac 下基于firebreath 开发多浏览器支持的浏览器插件 首先要区分什么是浏览器扩展和浏览器插件;插件可以像本地程序一样做的更多 一. 关于 firebreath http://www.fir ...

  6. 可以供MFC调用的,QT实现的DLL(使用qt-solutions的qtwinmigrate实现)

    MFC和QT的消息循环机制不同,所以,要让QT写的DLL可以供MFC调用,要做一点特殊的处理 #include <qmfcapp.h> #include <qwinwidget.h& ...

  7. 可以供MFC调用的,QT实现的DLL(qtwinmigrate实现)

    MFC和QT的消息循环机制不同,所以,要让QT写的DLL可以供MFC调用,要做一点特殊的处理 #include <qmfcapp.h> #include <qwinwidget.h& ...

  8. asp.net core系列 58 IS4 基于浏览器的JavaScript客户端应用程序

    一. 概述 本篇探讨使用"基于浏览器的JavaScript客户端应用程序".与上篇实现功能一样,只不过这篇使用JavaScript作为客户端程序,而非core mvc的后台代码Ht ...

  9. python selenium中如何测试360等基于chrome内核的浏览器

    转自:https://blog.csdn.net/five3/article/details/50013159 直接上代码,注意是基于chrome内核的浏览器,基于ie的请替换其中的chrome方法为 ...

随机推荐

  1. WordCount作业修改

    WordCount作业修改 github地址 需求说明 基本需求 功能说明 PSP 代码实现 字符总数查询 单词数查询 行数查询 总结 一.需求说明 1.基本需求 WordCount的需求可以概括为: ...

  2. ERP承接新后台优惠规则问题

    一.后台在哪配置优惠规则? 1.设置优惠时间段: 2.添加优惠活动: 关于自动和手动: 自动:创建后,ERP同步数据后即生效.     点餐,活动会自动生效,自动计算金额. 手动:创建后,ERP需要手 ...

  3. AI学习---特征工程【特征抽取、特征预处理、特征降维】

    学习框架 特征工程(Feature Engineering) 数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已 什么是特征工程: 帮助我们使得算法性能更好发挥性能而已 sklearn主 ...

  4. 安全之路 —— 使用Windows全局钩子打造键盘记录器

    简介 键盘记录功能一直是木马等恶意软件窥探用户隐私的标配,那么这个功能是怎么实现的呢?在Ring3级下,微软就为我们内置了一个Hook窗口消息的API,也就是SetWindowsHookEx函数,这个 ...

  5. java学习(权限修饰符)

    Java中,可以使用访问控制符来保护对类.变量.方法和构造方法的访问.Java 支持 4 种不同的访问权限. default (即缺省,什么也不写): 在同一包内可见,不使用任何修饰符.使用对象:类. ...

  6. Maven——settings.xml配置

    settings.xml配置 原文 <?xml version="1.0" encoding="UTF-8"?> <!-- Licensed ...

  7. 【Teradata 】TD最大列数

    1.一个表最大列数限制是多少? DB2,表最大列数1012,视图最大列数5000:一行最大长度32677Byte Teradata 表最大列数和视图最大列数2048,:16版本前,一行最大长度为64k ...

  8. 安装完最小化 RHEL/CentOS 7 后需要做的 30 件事情(一)转载自码农网

    CentOS 是一个工业标准的 Linux 发行版,是红帽企业版 Linux 的衍生版本.你安装完后马上就可以使用,但是为了更好地使用你的系统,你需要进行一些升级.安装新的软件包.配置特定服务和应用程 ...

  9. Java面试知识点之数据库篇(一)

    前言:数据库的相关知识,在面试中也经常出现,笔者认为非常有必要对此类知识进行相关总结. 1.索引 索引是对数据库表中一列或多列的值进行排序的结构,是帮助数据库高效获取数据的数据结构. 通俗理解:索引就 ...

  10. 冒泡排序 最好O(n)平均O(n^2) 选择排序O(n2) 插入排序O(n2)

    LOWB 三人组 分清有序区跟无序区 冒泡排序 思路: 首先,列表每两个相邻的数,如果前边的比后边的大,那么交换和两个数.... 冒泡排序优化 如果一趟没有发生任何交换 那么证明列表已经是有序的了 i ...