直接从Golang中将打印内容发送到外部打印机

huangapple go评论92阅读模式
英文:

Print to external printer directly from Golang

问题

我可以直接从Golang打印到我的(物理的、外部的)打印机上,而不使用打印机驱动程序、CUPS或任何其他复杂的东西吗?

英文:

Can I print directly to my (physical, external) printer from Golang, without using printer drivers or CUPS or any other such complexity?

答案1

得分: 7

是的!以下是使用IPP(Internet打印协议)从Golang打印PostScript文件的代码,大多数在过去20年中制造的网络打印机都支持:

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"

	"github.com/alexflint/go-arg"
	"github.com/kr/pretty"
	"github.com/phin1x/go-ipp"
)

func Main() error {
	var args struct {
		URI            string `arg:"positional,required"`
		PostscriptFile string `arg:"positional,required"`
	}
	arg.MustParse(&args)

	// 定义一个ipp请求
	req := ipp.NewRequest(ipp.OperationPrintJob, 1)
	req.OperationAttributes[ipp.AttributeCharset] = "utf-8"
	req.OperationAttributes[ipp.AttributeNaturalLanguage] = "en"
	req.OperationAttributes[ipp.AttributePrinterURI] = args.URI
	req.OperationAttributes[ipp.AttributeRequestingUserName] = "some-user"
	req.OperationAttributes[ipp.AttributeDocumentFormat] = "application/octet-stream"

	// 将请求编码为字节
	payload, err := req.Encode()
	if err != nil {
		return fmt.Errorf("error encoding ipp request: %w", err)
	}

	// 读取测试页面
	postscript, err := ioutil.ReadFile(args.PostscriptFile)
	if err != nil {
		return fmt.Errorf("error reading postscript file: %w", err)
	}
	payload = append(payload, postscript...)

	// 通过http将ipp请求发送到远程服务器
	httpReq, err := http.NewRequest("POST", args.URI, bytes.NewReader(payload))
	if err != nil {
		return fmt.Errorf("error creating http request: %w", err)
	}

	// 设置ipp头部
	httpReq.Header.Set("Content-Length", strconv.Itoa(len(payload)))
	httpReq.Header.Set("Content-Type", ipp.ContentTypeIPP)

	// 执行请求
	var httpClient http.Client
	httpResp, err := httpClient.Do(httpReq)
	if err != nil {
		return fmt.Errorf("error executing http request: %w", err)
	}
	defer httpResp.Body.Close()

	// 读取响应
	buf, err := io.ReadAll(httpResp.Body)
	if err != nil {
		return fmt.Errorf("error reading response body: %w", err)
	}

	// 对于成功的操作,响应必须为200
	// 其他可能的http代码有:
	// - 500 -> 服务器错误
	// - 426 -> 服务器请求加密连接
	// - 401 -> 禁止访问 -> 需要授权头部或用户没有权限
	if httpResp.StatusCode != 200 {
		return fmt.Errorf("printer said %d: %s", httpResp.StatusCode, buf)
	}

	// 解码ipp响应
	resp, err := ipp.NewResponseDecoder(bytes.NewReader(buf)).Decode(nil)
	if err != nil {
		return fmt.Errorf("error decoding ipp response: %w", err)
	}

	// 打印响应
	fmt.Println("Submitted print job. Response was:")
	pretty.Println(resp)
	return nil
}

要使用的URL只需为http://打印机的IP地址(此代码在Brother HL系列打印机上进行了测试)。

英文:

Yes! The following prints a postscript file from Golang using IPP (the internet printing protocol), which most network printers manufactured in the past 20 years support:

import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strconv"
"github.com/alexflint/go-arg"
"github.com/kr/pretty"
"github.com/phin1x/go-ipp"
)
func Main() error {
var args struct {
URI            string `arg:"positional,required"`
PostscriptFile string `arg:"positional,required"`
}
arg.MustParse(&args)
// define a ipp request
req := ipp.NewRequest(ipp.OperationPrintJob, 1)
req.OperationAttributes[ipp.AttributeCharset] = "utf-8"
req.OperationAttributes[ipp.AttributeNaturalLanguage] = "en"
req.OperationAttributes[ipp.AttributePrinterURI] = args.URI
req.OperationAttributes[ipp.AttributeRequestingUserName] = "some-user"
req.OperationAttributes[ipp.AttributeDocumentFormat] = "application/octet-stream"
// encode request to bytes
payload, err := req.Encode()
if err != nil {
return fmt.Errorf("error encoding ipp request: %w", err)
}
// read the test page
postscript, err := ioutil.ReadFile(args.PostscriptFile)
if err != nil {
return fmt.Errorf("error reading postscript file: %w", err)
}
payload = append(payload, postscript...)
// send ipp request to remote server via http
httpReq, err := http.NewRequest("POST", args.URI, bytes.NewReader(payload))
if err != nil {
return fmt.Errorf("error creating http request: %w", err)
}
// set ipp headers
httpReq.Header.Set("Content-Length", strconv.Itoa(len(payload)))
httpReq.Header.Set("Content-Type", ipp.ContentTypeIPP)
// perform the request
var httpClient http.Client
httpResp, err := httpClient.Do(httpReq)
if err != nil {
return fmt.Errorf("error executing http request: %w", err)
}
defer httpResp.Body.Close()
// read the response
buf, err := io.ReadAll(httpResp.Body)
if err != nil {
return fmt.Errorf("error reading response body: %w", err)
}
// response must be 200 for a successful operation
// other possible http codes are:
// - 500 -> server error
// - 426 -> sever requests a encrypted connection
// - 401 -> forbidden -> need authorization header or user is not permitted
if httpResp.StatusCode != 200 {
return fmt.Errorf("printer said %d: %s", httpResp.StatusCode, buf)
}
// decode ipp response
resp, err := ipp.NewResponseDecoder(bytes.NewReader(buf)).Decode(nil)
if err != nil {
return fmt.Errorf("error decoding ipp response: %w", err)
}
// print the response
fmt.Println("Submitted print job. Response was:")
pretty.Println(resp)
return nil
}

The URL to use is just http://ip-address-of-myprinter (this was tested on a brother HL-series printer)

huangapple
  • 本文由 发表于 2022年1月11日 01:52:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/70656837.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定