一、简介
- 云打印客户端是以独立进程和打印机交互(非作为浏览器插件进行打印)。
- 浏览器或其他客户端需要通过 WebSocket,协议与云打印客户端进行通信,支持javascript,java,c/c++,python等常用的语言(建议使用对应开发与语言支持的 Websockt 库)。
- 若ISV的ERP系统是B/S结构,建议使用如下版本浏览器:
- chrome 45及以上(建议使用chrome的最新版本);
- 相关浏览器的极速模式。
二、格式说明
{
"cmd": "command",
"requestID": "unique requestID",
"version": "1.0"
}
字段名 |
类型 |
说明 |
是否必须 |
cmd |
string |
请求的命令名称 |
是 |
requestID |
string |
请求的ID,用于唯一标识每个请求,每个客户端自己保证生成唯一ID,如UUID |
是 |
version |
string |
协议当前版本,当前为“1.0” |
是 |
{
"cmd": "command",
"requestID": "unique requestID"
}
字段名 |
类型 |
说明 |
cmd |
string |
请求的命令名称 |
requestID |
string |
发送请求中的ID,原封不动返回,使客户端能识别出哪个请求对应的响应 |
三、协议详解
1、获取打印机列表(getPrinters)
{
"cmd": "getPrinters",
"requestID": "123458976",
"version": "1.0"
}
{
"cmd": "getPrinters",
"requestID": "123458976",
"defaultPrinter": "XX快递打印机",
"printers": [{
"name": "XX快递打印机"
},
{
"name": "YY物流打印机"
}]
}
字段名 |
类型 |
说明 |
defaultPrinter |
string |
默认打印机 |
name |
string |
打印机的名字 |
2、获取打印机配置(getPrinterConfig)
{
"cmd":"getPrinterConfig",
"printer":"小红书打印机",
"version":"1.0",
"requestID":"123456789"
}
{
"cmd": "getPrinterConfig",
"requestID": "123456789",
"status": "success/failed",
"msg": "如果出错,错误原因",
"printer": {
"name": "打印机名称",
"needTopLogo": false,
"needBottomLogo": false,
"horizontalOffset": 1,
"verticalOffset": 2,
"forceNoPageMargins": true,
"autoPageSize": false,
"orientation": 0,
"autoOrientation": false,
"paperSize": {
"width": 100,
"height": 180
}
}
}
字段名 |
类型 |
说明 |
status |
string |
标示命令成功或失败,取值“success”或者“failed” |
msg |
string |
如果出错,错误原因 |
printer.name |
string |
打印机名称 |
printer.needTopLogo |
bool |
是否需要模板上联的快递logo true为需要 false为不需要 |
printer.needBottomLogo |
bool |
是否需要模板下联的快递logo true为需要 false为不需要 |
printer.horizontalOffset |
float |
水平偏移量 |
printer.verticalOffset |
float |
垂直偏移量 |
printer.forceNoPageMargins |
bool |
强制设置页面无空边 true为强制设置页面无空边 false为由打印机驱动决定 |
printer.paperSize.width |
int |
打印机纸张的宽度,单位是毫米 |
printer.paperSize.height |
int |
打印机纸张的高度,单位是毫米 |
printer. autoPageSize |
bool |
true:自适应纸张大小 false:不自适应 |
printer. orientation |
int |
0:纵向 1: 横向 |
printer. autoOrientation |
bool |
true:按照 orientation 适应纸张方向 alse:不自适应 |
3、设置打印机配置(setPrinterConfig)
{
"cmd": "setPrinterConfig",
"requestID": "123456789",
"version": "1.0",
"printer": {
"name": "小红书打印机",
"needTopLogo": true,
"needBottomLogo": false,
"horizontalOffset": 0.5,
"verticalOffset": 0.7,
"forceNoPageMargins": true,
"autoPageSize": false,
"orientation": 0,
"autoOrientation": false,
"paperSize": {
"width": 100,
"height": 180
}
}
}
注:参数说明参考获取打印机配置(getPrinterConfig)
{
"cmd":"setPrinterConfig",
"requestID":"123456789",
"status":"success",
"msg":"如果成功,则为空;如果失败,则为失败原因"
}
字段名 |
类型 |
说明 |
status |
string |
消息处理结果。success:成功;failed:失败 |
msg |
string |
如果成功,则为空;如果失败,则为失败原因 |
注:如果要保持某个配置不变,应省略对应的配置字段。 |
|
|
4、发送打印/预览数据协议(print)
注:因为打印机质量乘次不齐,建议 1 个 task 使用 一个 document,可以有效避免重打问题;
预览流程:
打印流程:
- 请求协议格式(密文数据,针对小红书电子面单)如下:
{
"cmd": "print",
"requestID": "123456789",
"version": "1.0",
"task": {
"taskID": "1",
"preview": false,
"printer": "",
"previewType": "pdf",
"firstDocumentNumber": 10,
"totalDocumentCount": 100,
"documents": [{
"documentID": "0123456789",
"contents": [{
"encryptedData":"AES:rU904rj6UH2oqfSUb43+Z+XlOkZaULeerkScS5xbmfjZC78uvsMTa3g6l33hRAz/srsk0TObjJaJI5n4tAPV1uv7szIPQGPDhwD6MK+zvTVIfuQCMC8p+cUB5S4FmqDhNE45LRVAlaoaI5YK8QmWK1WorhwnPxOFH4Ws/ApobtzDLDJaW6uu1AMEdAejEhRTWL3B1fRhhcDxc3gX+DZF9jJUB++fb9JZqmocWRu0Fvi/b1BokQx7Xt/N+FpJVRI0//NNUQ9b/W4tqGFIbf2IM/Ez1S5hBru5gKGdFzs99ZgCKqtWa0DnOzrZDXroU1mhurtlulE8QbipInu63fkIwn3h9ZSK0sMyV5Jrk5x3MIJDHeW9pc/Tw4TnKTAU134jl+GbbpYysa0+jBARWRjombeKIFSVfp/zgp15jClClUU1Nz4alTi22LimY2qteQRG6G/rCHiYxPoBRdrtqZZxNSdnKG5yjSdtA2CEL1DJNg1QkFVSSsOuqcHLdrKl6oMR+aUN6wM3GQikmKSU/CH4hWCCXxFaJXvBYoSxZ63GrM/d+l6D4+9+rCxHJoEVsa2E1TMHLUOnN6CweSM+45lcBK19bbCUJDyky6nb1NbxrZGYhmfkrNzE2GN+Cz4iTAgxJlQxd1gVvS4v5nB7qNfb0Uhy9NTopdumxOS7NXFFg3RFdBfAJ0nLGnxECUvUihBC3pwsLGimrUnIF4174m6J6Ga6cQE+Pp1LXgtKf5zWJdWHkm2vQhazcAsQC8JJZFb1ESp1vIAvpy0d0YmGrLLzxWNciHlOa7vguFCVF3UbTFe8r1Mxyym9rqNrZDXWRtBija9yeliMERVFuOTRjlc0PVAzveexQmuD4ESTzMZPtbO0jos1EITKhHcV35Na7E4I7bEe3L2u5yuFuzDA5cc8OA8v761+xOI70bGXUwvFO2kCCiUFEzI9ksLIDTtydBTA94lf4MYH6m0ziRmAhAgcwm5QJFd2G4JzpFIK4+dLuEZamrYUcnHmWzDIg+HYIXh6g3S2maFU7dUtwYoerptOTiVg8FxRlUTx30NDTgjm7ll8vEJXHj7yd/gAO3Vm9P54OSMv8w+pzX3gtCkvthrkjlToT1jMRNJyuJAeSBf5jruzYLS68inlSE/ehT10zhaiBvaCqojZZ2Ux0JQGhbR/nQ==",
"signature":"19d6f7759487e556ddcdd3d499af087080403277b7deed1a951cc3d9a93c42a7e22ccba94ff609976c5d3ceb069b641f541bc9906098438d362cae002dfd823a8654b2b4f655e96317d7f60eef1372bb983a4e3174cc8d321668c49068071eaea873071ed683dd24810e51afc0bc925b7a2445fdbc2034cdffb12cb4719ca6b7",
"templateURL":"http://cloudprint.cainiao.com/template/standard/101/123",
"ver":"waybill_print_secret_version_1"
},
{
"data": {
"value": "测试字段值需要配合自定义区变量名"
},
"templateURL": "http://cloudprint.cainiao.com/template/customArea/440439"
}]
}]
}
}
{
"cmd": "print",
"requestID": "123456789",
"version": "1.0",
"task": {
"taskID": "1",
"preview": false,
"printer": "",
"previewType": "pdf",
"firstDocumentNumber": 10,
"totalDocumentCount": 100,
"documents": [{
"documentID": "0123456789",
"contents": [{
"data": {
"nick": "张三"
},
"templateURL": "http://cloudprint.cainiao.com/template/standard/278250/1"
},
{
"data": {
"value": "测试字段值需要配合自定义区变量名"
},
"templateURL": "http://cloudprint.cainiao.com/template/customArea/440439"
}]
}]
}
}
字段名 |
类型 |
说明 |
是否必须 |
taskID |
string |
打印机任务ID,每个打印任务会分配不同的且唯一的ID |
是 |
notifyType |
array |
打印通知类型:“render”, “print” [“render”] : 仅渲染响应 notify [“print”] : 仅出纸响应 notify “render”, “print” : 渲染完成会响应 notify && 出纸完成后会响应 notify [] : 不允许 注:如果notifyType没有指定,默认为[“render”, “print”] |
否 |
preview |
bool |
是否预览.true为预览,false为打印 |
是 |
previewType |
string |
属性取值“pdf” or “image” 预览模式,是以pdf还是image方式预览,二选一,此属性不是必选,默认以pdf预览。 |
否 |
firstDocumentNumber |
int |
task 起始 document 序号 |
否 |
totalDocumentCount |
int |
task document 总数 |
否 |
printer |
string |
打印机名,如果为空,会使用默认打印机 |
否 |
templateURL |
string |
模板文件url |
是 |
signature |
string |
模板与数据的签名 |
否 |
documents |
array |
文档数组,每个数据表示一页 |
是 |
documentID |
string |
文档的唯一ID,对于小红书标准面单来讲,就是面单号;如果是自定义模板,需要保证唯一 |
是 |
data |
Json Object |
模板需要的打印数据 |
是 |
{
"cmd":"print",
"requestID":"123458976",
"taskID":"1",
"status":"success", //如果是打印,表示打印任务提交成功,如果是预览,表示预览PDF文件生成成功
"previewURL":"http://127.0.0.1/previewxxx.pdf", //如果是预览,会返回这个属性,表示预览PDF文件的URL地址,如果是打印命令,不返回此属性
//如果是预览并且预览模式是previewType:image,会返回这个属性,表示预览图片的URL地址,如果是打印命令,不返回此属性
"previewImage": [
"http://127.0.0.1/preview1.jpg",
"http://127.0.0.1/preview2.jpg",
"http://127.0.0.1/preview3.jpg"
]
}
字段名 |
类型 |
说明 |
taskID |
string |
打印机任务ID,每个打印任务会分配不同的且唯一的ID |
status |
string |
如果是打印,表示打印任务提交成功,如果是预览,表示预览PDF文件生成成功 |
previewURL |
string |
可预览的PDF文件URL路径 |
previewImage |
string[] |
预览image的URL路径,是一个字符串数组 |
注: |
|
|
- 如果是打印命令,只是表示将打印任务提交到打印队列,会快速返回。
- 如果是预览命令,需要将预览文件生成,才会返回,需要一段等待时间。
5、打印通知(notifyPrintResult)
{
"cmd":"notifyPrintResult",
"printer":"中通打印机A",
"taskID":"1",
"taskStatus":"printed",
"printStatus":[
{
"documentID":"9890000112011",
"status":"success",
"msg":"if failed,some tips, if success ,nothing",
"detail":"错误信息的补充描述"
}
]
}
字段名 |
类型 |
说明 |
documentID |
string |
文档的唯一ID,对于小红书标准面单来讲,就是面单号;如果是自定义模板,需要保证唯一 |
taskStatus |
string |
任务状态: failed : 失败 rendered: 渲染完成 printed : 出纸完成 注:当打印出纸之后才会发送通知并且只通知一次 |
status |
string |
任务状态:success成功;failed 失败,canceled 取消 (当一个任务中的一个文档打印失败,任务中其他的文档打印状态为“canceled”状态) |
msg |
string |
如果任务状态为成功或挂起为空,如果任务状态为失败,则为失败原因概要。 |
detail |
string |
错误信息的补充描述 |
printer |
string |
负责打印的打印机名 |
taskID |
string |
任务ID,每个打印任务会分配不同的且唯一的ID |
6、获取任务打印任务状态(getTaskStatus)
{
"cmd":"getTaskStatus",
"requestID":"123458976",
"version":"1.0",
"taskID":[
"12311",
"12312"
]
}
字段名 |
类型 |
说明 |
是否必须 |
taskID |
json数组 |
打印机任务ID列表 |
是 |
{
"cmd":"getTaskStatus",
"requestID":"123458976",
"printStatus":[
{
"taskID":"12312",
"detailStatus":[
{
"documentID":"9890000112011",
"status":"success",
"msg":"if failed ,some tips, if success or pending nothing",
"printer":"中通打印机A"
}
]
}
]
}
字段名 |
类型 |
说明 |
taskID |
string |
打印机任务ID,每个打印任务会分配不同的且唯一的ID |
documentID |
string |
文档的唯一ID,对于小红书标准面单来讲,就是面单号;如果是自定义模板,需要保证唯一 |
status |
string |
任务状态:success成功;failed失败;pending,提交到打印机打印队列 |
msg |
string |
如果任务状态为成功或挂起为空,如果任务状态为失败,则为失败原因。 |
printer |
string |
负责打印的打印机名 |
7、获取全局配置(getGlobalConfig)
{
"cmd":"getGlobalConfig",
"requestID":"12345678901",
"version":"1.0"
}
{
"cmd":"getGlobalConfig",
"requestID":"12345678901",
"status":"success",
"msg":"return nothing when success, return some tips when failed",
"notifyOnTaskFailure":true
}
字段名 |
类型 |
说明 |
status |
string |
表示命令成功或失败,取值“success”或者“failed” |
msg |
string |
如果出错,错误原因 |
notifyOnTaskFailure |
bool |
打印任务失败时是否需要通知(弹出对话框提醒用户打印失败原因并默认暂停当前打印机的打印),true为需要,false为不需要 |
8、设置全局配置(setGlobalConfig)
{
"cmd":"setGlobalConfig",
"requestID":"12345678901",
"version":"1.0",
"notifyOnTaskFailure":true
}
{
"cmd":"setGlobalConfig",
"requestID":"12345678901",
"status":"success",
"msg":"return nothing when success, return some tips when failed"
}
字段名 |
类型 |
说明 |
status |
string |
表示命令成功或失败,取值“success”或者“failed” |
msg |
string |
如果出错,错误原因 |
notifyOnTaskFailure |
bool |
打印任务失败时是否需要通知(弹出对话框提醒用户打印失败原因并默认暂停当前打印机的打印),true为需要,false为不需要 |
9、获取客户端版本信息(getAgentInfo)
{
"cmd":"getAgentInfo",
"requestID":"12345678901",
"version":"1.0"
}
{
"cmd":"getAgentInfo",
"requestID":"12345678901",
"status":"success",
"msg":"return nothing when success, return some tips when failed",
"version":"0.2.8.3"
}
字段名 |
类型 |
说明 |
status |
string |
表示命令成功或失败,取值“success”或者“failed” |
msg |
string |
如果出错,错误原因 |
version |
string |
版本号 |
四、注意事项
五、使用示例
1、JavaScript使用示例
function doConnect()
{
socket = new WebSocket(\'ws://localhost:14528\');
//如果是https的话,端口是14529
//socket = new WebSocket(\'wss://localhost:14529\');
// 打开Socket
socket.onopen = function(event)
{
// 监听消息
socket.onmessage = function(event)
{
console.log(\'Client received a message\',event);
};
// 监听Socket的关闭
socket.onclose = function(event)
{
console.log(\'Client notified socket has closed\',event);
};
};
}
/***
*
* 获取请求的UUID,指定长度和进制,如
* getUUID(8, 2) //"01001010" 8 character (base=2)
* getUUID(8, 10) // "47473046" 8 character ID (base=10)
* getUUID(8, 16) // "098F4D35"。 8 character ID (base=16)
*
*/
function getUUID(len, radix) {
var chars = \'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\'.split('');
var uuid = [], i;
radix = radix || chars.length;
if (len) {
for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
} else {
var r;
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = \'4\';
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random()*16;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
}
return uuid.join('');
}
/***
* 构造request对象
*/
function getRequestObject(cmd){
var request = new Object();
request.requestID=getUUID(8, 16);
request.version="1.0";
request.cmd=cmd;
return request;
}
/***
* 获取自定义区数据以及模板URL
* waybillNO 电子面单号
*/
function getCustomAreaData(var waybillNO){
//获取waybill对应的自定义区的JSON object,此处的ajaxGet函数是伪代码
var jsonObject = ajaxGet(waybillNO);
var ret = new Object();
ret.templateURL=jsonObject.content.templateURL;
ret.data=jsonObject.content.data;
return ret;
}
/***
* 获取电子面单Json 数据
* waybillNO 电子面单号
*/
function getWaybillJson(var waybillNO){
//获取waybill对应的json object,此处的ajaxGet函数是伪代码
var jsonObject = ajaxGet(waybillNO);
return jsonObject;
}
/**
* 请求打印机列表demo
* */
var request = getRequestObject("getPrinters");
webSocket.send(JSON.stringify(request));
/**
* 弹窗模式配置打印机
* */
var request = getRequestObject("printerConfig");
webSocket.send(JSON.stringify(request));
/**
* 打印电子面单
* printer 指定要使用那台打印机
* waybillArray 要打印的电子面单的数组
*/
function doPrint(var printer,var waybillArray)
{
var request = getRequestObject("print");
request.task = new Object();
request.task.taskID = getUUID(8,10);
request.task.preview = false;
request.task.printer = printer;
var documents = new Array();
for(i=0;i<waybillArray.length;i++) {
var doc = new Object();
doc.documentID = waybillArray[i];
var content = new Array();
var waybillJson = getWaybillJson(waybillArray[i]);
var customAreaData = getCustomAreaData(waybillArray[i]);
content.push(waybillJson,customAreaData);
doc.content = content;
documents.push(doc);
}
request.task.documents=documents;
socket.send(JSON.stringify(request));
}
/**
* 响应请求demo
* */
websocket.onmessage = function(event){
var response = eval(event.data);
if (response.cmd == \'getPrinters\') {
getPrintersHandler(response);//处理打印机列表
} else if (response.cmd == \'printerConfig\') {
printConfigHandler(response);
}
};
2、JAVA使用示例
java使用websocket需要引入第三方库 下载地址 。
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.3.0</version>
</dependency>
自己创建一个websocket管理类,需要继承自第三方类库的WebSocketClient:
import java.net.URI;
import java.net.URISyntaxException;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.drafts.Draft_17;
import org.java_websocket.handshake.ServerHandshake;
public class WebSocketClientManager extends WebSocketClient {
static WebSocketClientManager webSocket = null;
public static void main(String[] args) throws URISyntaxException {
String uri = "ws://127.0.0.1:14528";
webSocket = new WebSocketClientManager(new URI(uri), new Draft_17());
//建立连接
webSocket.connect();
}
public WebSocketClientManager(URI serverUri, Draft draft) {
super(serverUri, draft);
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
//获取打印机列表
String getPrinterListCmd = "{\"requestID\":\"12345678901234567890\",\"verson\":\"1.0\",\"cmd\":\"getPrinters\"}";
webSocket.send(getPrinterListCmd);
//发送打印任务
String printCmd = "打印任务报文,内容过长此处不粘贴";
webSocket.send(printCmd);
}
//WebSocket回调函数
@Override
public void onMessage(String message) {
//TODO 对打印服务返回的数据进行处理
System.out.println(message);
}
@Override
public void onClose(int i, String s, boolean b) {
}
@Override
public void onError(Exception e) {
}
}