英文:
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)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论