HTML通过Websocket发送页面内容给打印客户端
1、概述
需求如下: 网页将当前html内容通过ws发送给客户端程序,调用本地打印机打印网页内容。
2、客户端程序
- demo 实现ws的服务端
结构如下:
代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
namespace Fleck.Samples.ConsoleApp
{
class Server
{
static void Main()
{
FleckLog.Level = LogLevel.Debug;
var allSockets = new List<IWebSocketConnection>();
var server = new WebSocketServer("ws://0.0.0.0:8181");
server.Start(socket =>
{
socket.OnOpen = () =>
{
Console.WriteLine("Open!");
allSockets.Add(socket);
};
socket.OnClose = () =>
{
Console.WriteLine("Close!");
allSockets.Remove(socket);
};
socket.OnMessage = message =>
{
Console.WriteLine("123");
Console.WriteLine(message);
allSockets.ToList().ForEach(s => s.Send("Echo: " + message));
};
});
var input = Console.ReadLine();
while (input != "exit")
{
foreach (var socket in allSockets.ToList())
{
socket.Send(input);
}
input = Console.ReadLine();
}
}
}
}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>websocket client</title>
<script type="text/javascript">
var start = function () {
var inc = document.getElementById('incomming');
var wsImpl = window.WebSocket || window.MozWebSocket;
var form = document.getElementById('sendForm');
var input = document.getElementById('sendText');
inc.innerHTML += "开始连接ws服务 .....<br/>";
// create a new websocket and connect
window.ws = new wsImpl('ws://localhost:8181/');
// when data is comming from the server, this metod is called
ws.onmessage = function (evt) {
inc.innerHTML += evt.data + '<br/>';
};
// when the connection is established, this method is called
ws.onopen = function () {
inc.innerHTML += '服务已连接<br/>';
};
// when the connection is closed, this method is called
ws.onclose = function () {
inc.innerHTML += '连接关闭<br/>';
}
form.addEventListener('submit', function(e){
e.preventDefault();
var val = input.value;
ws.send(val);
input.value = "";
});
}
window.onload = start;
</script>
</head>
<body>
<form id="sendForm">
<input id="sendText" placeholder="Text to send" />
</form>
<pre id="incomming"></pre>
</body>
</html>
-
客户端集成打印和ws服务
结构如下:
界面如下:
代码如下:
PrintMain.cs
using Fleck;
using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace Print
{
public partial class PrintMain : Form
{
public PrintMain()
{
InitializeComponent();
this.WindowState = FormWindowState.Maximized;
var server = new WebSocketServer("ws://0.0.0.0:8181");
server.Start(socket =>
{
Socket = (WebSocketConnection)socket;
socket.OnOpen = () =>
{
Console.WriteLine("Open!");
};
socket.OnClose = () =>
{
Console.WriteLine("Close!");
};
socket.OnMessage = message =>
{
Console.WriteLine(message);
GetHtmlTemplate(message);
this.webBrowser.Url = new Uri(Application.StartupPath.ToString() + "\\" + "Demo.html");
};
});
this.webBrowser.Url = new Uri(Application.StartupPath.ToString() + "\\" + "Demo.html");
//当加载文件完成后激发手动追加的事件
this.webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(PrintDocument);
this.webBrowser.Focus();
}
public WebBrowser PrintObject { get; set; }
public IWebSocketConnection Socket { get; set; }
private void PrintSet_Click(object sender, EventArgs e)
{
if (PrintObject != null)
{
PrintObject.ShowPrintDialog();
}
else
{
MessageBox.Show("无内容可打印!");
}
}
private void PageSet_Click(object sender, EventArgs e)
{
//弹出页面设置对话框-----即上方定义的新进程需要处理的窗口
if (PrintObject != null)
{
PrintObject.ShowPageSetupDialog();
}
else
{
MessageBox.Show("无内容可打印!");
}
}
private void PrintView_Click(object sender, EventArgs e)
{
if (PrintObject != null && !PrintObject.DocumentText.Contains("&template&"))
{
PrintObject.ShowPrintPreviewDialog();
}
else
{
MessageBox.Show("无内容可预览!");
}
}
private void btnPrint_Click(object sender, EventArgs e)
{
if (PrintObject != null && !PrintObject.DocumentText.Contains("&template&"))
{
PrintObject.Print();
PrintObject.Dispose();
}
else
{
MessageBox.Show("无内容可打印!");
}
}
private void PrintDocument(object sender, WebBrowserDocumentCompletedEventArgs e)
{
PrintObject = ((WebBrowser)sender);
if (PrintObject != null && !PrintObject.DocumentText.Contains("&template&"))
{
PrintObject.Print();
}
}
private void btnPrintProp_Click(object sender, EventArgs e)
{
if (PrintObject != null)
{
PrintObject.ShowPropertiesDialog();
//MessageBox.Show(AppDomain.CurrentDomain.BaseDirectory);
}
else
{
MessageBox.Show("无内容可打印!");
}
}
//读取模板并替换传输过来的html内容
private void GetHtmlTemplate(string message)
{
Stream myStream = new FileStream(Application.StartupPath.ToString() + "\\" + "Demo.html", FileMode.Open);
Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
StreamReader myStreamReader = new StreamReader(myStream, encode);
string strhtml = myStreamReader.ReadToEnd();
string stroutput = strhtml.Replace("&template&", message);
myStream.Seek(0, SeekOrigin.Begin);
myStream.SetLength(0);
StreamWriter sw = new StreamWriter(myStream, encode);
sw.Write(stroutput);
sw.Flush();
sw.Close();
myStream.Close();
}
private void btnSendMsgToClient_Click(object sender, EventArgs e)
{
if (Socket != null)
{
Socket.Send("服务端发送的消息:" + this.txtServerMsg.Text + "\r\n");
this.txtServerMsg.Text = "";
}
}
}
}
Demo.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
&template&
</body>
</html>
3、Html发送内容
handlePrint() {
if (this.isReport) {
//打印
var dom = document.getElementsByClassName('ppxpxpxpxppxpx')
console.log('打印', this.readScaleSize(dom))
let oldScaleSize = this.readScaleSize(dom)
this.scaleToSize(1, dom)
// ****************连接websocket 打印服务 strat****************
console.log('dom: ', dom)
let checkboxs = dom[0].getElementsByClassName('ant-checkbox ant-checkbox-checked')
if (checkboxs && checkboxs.length > 0) {
for (let index = 0; index < checkboxs.length; index++) {
const element = checkboxs[index]
let checkbox = element.children[0]
checkbox.setAttribute('checked', 'checked')
}
}
var arr = Array.prototype.slice.call(dom)
console.log('arr: ', arr)
this.websocket = new WebSocket('ws://localhost:8181/')
this.handleWebsocket(arr[0].innerHTML)
// ****************连接websocket 打印服务 end****************
$(dom).printThis({
debug: false, // show the iframe for debugging
importCSS: true, // import parent page css
importStyle: true, // import style tags
printContainer: true, // print outer container/$.selector
loadCSS: '', // path to additional css file - use an array [] for multiple
pageTitle: '', // add title to print page
removeInline: false, // remove inline styles from print elements
removeInlineSelector: '*', // custom selectors to filter inline styles. removeInline must be true
printDelay: 333, // variable print delay
header: null, // prefix to html
footer: null, // postfix to html
base: true, // preserve the BASE tag or accept a string for the URL
formValues: true, // preserve input/form values
canvas: true, // copy canvas content
doctypeString: '', // enter a different doctype for older markup
removeScripts: false, // remove script tags from print content
copyTagClasses: true, // copy classes from the html & body tag
beforePrintEvent: () => {
this.scaleToSize(oldScaleSize, dom)
}, // function for printEvent in iframe
beforePrint: null, // function called before iframe is filled
afterPrint: null // function called before iframe is removed
})
}
},
4、总结
- 前端我们用的AntDesign+vue,传递Checkbox、Radio这些组件的时候,做了特殊处理,转为原生的选中效果。
let checkboxs = dom[0].getElementsByClassName('ant-checkbox ant-checkbox-checked')
if (checkboxs && checkboxs.length > 0) {
for (let index = 0; index < checkboxs.length; index++) {
const element = checkboxs[index]
let checkbox = element.children[0]
checkbox.setAttribute('checked', 'checked')
}
}
- 网页端我们也支持浏览器自身的打印,用的是printThis库,遇到的问题也是关于Checkbox,选中效果无法呈现,源代码加了这句代码 -webkit-print-color-adjust: exact;
let bodyStyle = '<style type="text/css">body {height: auto !important;-webkit-print-color-adjust: exact;}</style>'
$head.append(bodyStyle)
- 关于ws的心跳机制,因为项目要做剔下线的功能,不允许二次登录。
处理方式:数据接收,保持与后端的通信, 约定:前端 ping 后端 pong
前端发送一个心跳ping,后端收到后,返回一个心跳消息 pong,重新连接ws。 - 客户端的打印主要用的是webBrowser集成网页打印,通过占位符替换网页内容。
- 客户端websocket使用 Fleck第三方库。