在Web的项目中,前端界面往往会有一些打印功能,本身浏览器的打印功能也挺好用的,但是浏览器一般是输出到普通A4打印机,如果需要打印一些便签之类,需要用到蓝牙打印机,这时这个打印功能就无能为力了。幸好我发现了一个Webbluetooth的项目,这个项目也是一个谷歌官方发起的项目,实现网页对蓝牙的调用,目前Chrome系较高版本浏览器支持。
要使用Web蓝牙功能需要一个usb口的蓝牙适配器,可以在Chrome浏览器中输入"chrome://bluetooth-internals"查看蓝牙设备状态,该调试功能非常强大!

android的蓝牙打印 通过蓝牙打印文件_HTTP


通过UUID可以查看到所有的蓝牙设备、service、Characteristics,以及读写蓝牙设备

android的蓝牙打印 通过蓝牙打印文件_HTTP_02


蓝牙打印机连接上后不是发文本就能直接打印,而是要转换成打印指令,一般是通过ESC/POS通用打印指令传递打印指令的,就是说我们要输出的文本、二维码、图片等内容需要转换成ESC/POS指令。目前大多数蓝牙打印机都是支持ESC/POS指令的,指令可以看https://www.jianshu.com/p/dd6ca0054298 了解下,百度上还有专门详尽的指令集说明,不过我们不需要自己来写指令转换代码,我找到一个js库,很方便的实现了ESC/POS编码,https://www.npmjs.com/package/@freedom_sky/esc-pos-encoder

Webbluetooth如何应用,,这里有个文档简单说明。https://bluetooth.rocks/tweets/这个则是有案例,也可以把代码扒下来,我将它改写成一个模块

`

class BluetoothPrinter {

constructor() {

this._EVENTS = {};

this._CHARACTERISTIC = null;

this._QUEUE = [];

this._WORKING = false;

}

connect() {
	console.log('Requesting Bluetooth Device...');
	
	return new Promise((resolve, reject) => {
	
		navigator.bluetooth.requestDevice({
			filters: [{ services: ['000018f0-0000-1000-8000-00805f9b34fb'] }]
		})
			.then(device => {
				console.log('Connecting to GATT Server...');

				device.addEventListener('gattserverdisconnected', this._disconnect.bind(this));
				return device.gatt.connect();
			})
			.then(server => server.getPrimaryService("000018f0-0000-1000-8000-00805f9b34fb"))
			.then(service => service.getCharacteristic("00002af1-0000-1000-8000-00805f9b34fb"))
			.then(characteristic => {
				this._CHARACTERISTIC = characteristic;
				resolve();
			})
			.catch(error => {
				console.log('Could not connect! ' + error);
				reject();
			});			
	});
	
}

print(command) {
	const maxLength = 100;
	let chunks = Math.ceil(command.length / maxLength);

	if (chunks === 1) {
		this._queue(command);
	} else {
		for (let i = 0; i < chunks; i++) {
			let byteOffset = i * maxLength;
			let length = Math.min(command.length, byteOffset + maxLength);
			this._queue(command.slice(byteOffset, length));
		}
	}
}

_queue(f) {
	var that = this;
	
	function run() {
		if (!that._QUEUE.length) {
			that._WORKING = false; 
			return;
		}
		
		that._WORKING = true;
		that._CHARACTERISTIC.writeValue(that._QUEUE.shift()).then(() => run() );
	}
	
	that._QUEUE.push(f);
	
	if (!that._WORKING) run();	
}

addEventListener(e, f) {
	this._EVENTS[e] = f;
}

isConnected() {
	return !! this._CHARACTERISTIC;
}
	
_disconnect() {
	console.log('Disconnected from GATT Server...');

	this._CHARACTERISTIC = null;
	
	if (this._EVENTS['disconnected']) {
		this._EVENTS['disconnected']();
	}
}

};

module.exports = BluetoothPrinter;

`

使用的时候直接require进来,在调用的过程中却总是出错

android的蓝牙打印 通过蓝牙打印文件_android的蓝牙打印_03


Could not connect! SecurityError: Failed to execute 'requestDevice' on 'Bluetooth': Must be handling a user gesture to show a permission request.

用必应查了一遍文章解决了这个问题https://web.dev/bluetooth/,原来出来安全性的考虑,一定需要用户交互才能使用Web蓝牙,就是说必须是通过点击按钮等用户行为才能有权限打开蓝牙,创建一个index.js

`let EscPosEncoder = require('esc-pos-encoder');
let BluetoothPrinter = require('./bluetooth-printer');

window.EscPosEncoder=new EscPosEncoder();
window.BluetoothPrinter = new BluetoothPrinter();
`

然后再创建index.html

`

蓝牙打印

` 用webpack打包运行,成功打印。

还有一个要注意的点,webbluetooth在本地可以直接调用,但部署上服务器一定需要HTTPS支持(其他硬件方面的web调用也同理),如果没有的话会出现

android的蓝牙打印 通过蓝牙打印文件_Server_04


查看HTTP和HTTPS下的naviagtor,HTTP下是没有bluetooth等成员的

android的蓝牙打印 通过蓝牙打印文件_HTTP_05

android的蓝牙打印 通过蓝牙打印文件_Web_06