使用 C# 的 Google 云打印
Posted
技术标签:
【中文标题】使用 C# 的 Google 云打印【英文标题】:Google Cloud Print using C# 【发布时间】:2011-12-12 08:42:14 【问题描述】:我尝试使用 C# 使用 Google 云打印。互联网上只有一个example,他写了 Josh Goebel。 我不会发布完整的示例,这是发送文件进行打印的唯一方法:
public CloudPrintJob PrintDocument(string printerId, string title, byte[] document)
try
string authCode;
if (!Authorize(out authCode))
return new CloudPrintJob() success = false ;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.google.com/cloudprint/submit?output=json");
request.Method = "POST";
string queryString =
"printerid=" + HttpUtility.UrlEncode(printerId) +
"&capabilities=" + HttpUtility.UrlEncode("") +
"&contentType=" + HttpUtility.UrlEncode("application/pdf") +
"&title=" + HttpUtility.UrlEncode(title) +
"&content=" + HttpUtility.UrlEncode(Convert.ToBase64String(document));
byte[] data = new ASCIIEncoding().GetBytes(queryString);
request.Headers.Add("X-CloudPrint-Proxy", Source);
request.Headers.Add("Authorization", "GoogleLogin auth=" + authCode);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
Stream stream = request.GetRequestStream();
stream.Write(data, 0, data.Length);
stream.Close();
// Get response
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(CloudPrintJob));
MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(responseContent));
CloudPrintJob printJob = serializer.ReadObject(ms) as CloudPrintJob;
return printJob;
catch (Exception ex)
return new CloudPrintJob() success = false, message = ex.Message ;
我运行这段代码,然后我的打印机有一个界面,但没有发生打印。我的打印机界面说要打印的页数为0,文件大小 与我发送给打印机的不符。
谷歌云打印说任务(作业)添加成功,但在谷歌云打印界面文档名称旁边显示“错误”。
我认为 HttpUtility.UrlEncode 或 Convert.ToBase64String 可能有问题,但我尝试了反向转换 - 一切正常。
有人有什么想法吗?
【问题讨论】:
我通过 Twitter 写信给 Josh Goebel - 他说了没有写这个例子的东西。嘿,认识这个程序员的人,谁写了这个? :) 已经很久了,所以我忘记了答案:) 但是我看看我的代码和我看到的(只是与上面的代码比较):“&contentType =” + HttpUtility.UrlEncode(“ url") + "&content=" + HttpUtility.UrlEncode(content);我还删除了字符串 responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd(); 之后的所有代码 【参考方案1】:我很欣赏这个问题现在有点老了,但我最近不得不看看这个我在工作中做的事情,虽然发布的示例代码让我朝着正确的方向前进,但它确实让我很一段时间让它完全工作。
“列表打印机”功能如所述工作正常,但我无法让提交正常工作,它只是直接进入错误,如 OP 所述。
在使用 Fiddler 在 Chrome 中分析实际提交请求并查看 google 网站上的 php 示例代码后,我发现提交需要是多部分 MIME 消息,否则将无法正常工作。
这是一个 HTTP POST 消息示例,它最终使提交操作正常工作。注意gcp Printer Id需要在请求URL中传递,数据(无论是PDF、JPG还是PNG)需要经过Base64编码并设置正确的mime类型(application/pdf、image/jpeg.. .) 对于“内容”块(下面列表中的最后一个):
POST http://www.google.com/cloudprint/submit?printerid=<printerid>&output=json HTTP/1.1
Host: www.google.com
Content-Length: 44544
X-CloudPrint-Proxy: Google-JS
Content-Type: multipart/form-data; boundary=----CloudPrintFormBoundaryqeq6g6ncj5v7
------CloudPrintFormBoundaryqeq6g6ncj5v7
Content-Disposition: form-data; name="capabilities"
"capabilities":[]
------CloudPrintFormBoundaryqeq6g6ncj5v7
Content-Disposition: form-data; name="contentType"
dataUrl
------CloudPrintFormBoundaryqeq6g6ncj5v7
Content-Disposition: form-data; name="title"
zodiac-pig-pic.jpg
------CloudPrintFormBoundaryqeq6g6ncj5v7
Content-Disposition: form-data; name="content"
data:image/jpeg;base64,JVBERi0xLjQKJeHp69MKMiAwIG...2NgolJUVPRg==
------CloudPrintFormBoundaryqeq6g6ncj5v7--
让我大吃一惊的是,标题中指定的边界值比它们的实际用法少了 2 个连字符 (--)(当你盯着一些想知道它为什么不工作的东西时不明显!),最后一个边界实例的末尾有两个额外的连字符,我需要去掉 C# Expect100Continue 标头(通过使用 request.ServicePoint.Expect100Continue = false)。
更新:这是我当时写的完整代码,以防它帮助任何人。
using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;
using GoogleCloudPrintServices.DTO;
namespace GoogleCloudPrintServices.Support
public class GoogleCloudPrint
public string UserName get; set;
public string Password get; set;
public string Source get; set;
private const int ServiceTimeout = 10000;
public GoogleCloudPrint (String source)
Source = source;
public CloudPrintJob PrintDocument (string printerId, string title, byte[] document, String mimeType)
try
string authCode;
if (!Authorize (out authCode))
return new CloudPrintJob success = false ;
var b64 = Convert.ToBase64String (document);
var request = (HttpWebRequest)WebRequest.Create ("http://www.google.com/cloudprint/submit?output=json&printerid=" + printerId);
request.Method = "POST";
// Setup the web request
SetupWebRequest (request);
// Add the headers
request.Headers.Add ("X-CloudPrint-Proxy", Source);
request.Headers.Add ("Authorization", "GoogleLogin auth=" + authCode);
var p = new PostData ();
p.Params.Add (new PostDataParam Name = "printerid", Value = printerId, Type = PostDataParamType.Field );
p.Params.Add (new PostDataParam Name = "capabilities", Value = "\"capabilities\":[]", Type = PostDataParamType.Field );
p.Params.Add (new PostDataParam Name = "contentType", Value = "dataUrl", Type = PostDataParamType.Field );
p.Params.Add (new PostDataParam Name = "title", Value = title, Type = PostDataParamType.Field );
p.Params.Add (new PostDataParam
Name = "content",
Type = PostDataParamType.Field,
Value = "data:" + mimeType + ";base64," + b64
);
var postData = p.GetPostData ();
Trace.WriteLine (postData);
byte[] data = Encoding.UTF8.GetBytes (postData);
request.ContentType = "multipart/form-data; boundary=" + p.Boundary;
Stream stream = request.GetRequestStream ();
stream.Write (data, 0, data.Length);
stream.Close ();
// Get response
var response = (HttpWebResponse)request.GetResponse ();
var responseContent = new StreamReader (response.GetResponseStream ()).ReadToEnd ();
var serializer = new DataContractJsonSerializer (typeof (CloudPrintJob));
var ms = new MemoryStream (Encoding.Unicode.GetBytes (responseContent));
var printJob = serializer.ReadObject (ms) as CloudPrintJob;
return printJob;
catch (Exception ex)
return new CloudPrintJob success = false, message = ex.Message ;
public CloudPrinters Printers
get
var printers = new CloudPrinters ();
string authCode;
if (!Authorize (out authCode))
return new CloudPrinters success = false ;
try
var request = (HttpWebRequest)WebRequest.Create ("http://www.google.com/cloudprint/search?output=json");
request.Method = "POST";
// Setup the web request
SetupWebRequest (request);
// Add the headers
request.Headers.Add ("X-CloudPrint-Proxy", Source);
request.Headers.Add ("Authorization", "GoogleLogin auth=" + authCode);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = 0;
var response = (HttpWebResponse)request.GetResponse ();
var responseContent = new StreamReader (response.GetResponseStream ()).ReadToEnd ();
var serializer = new DataContractJsonSerializer (typeof (CloudPrinters));
var ms = new MemoryStream (Encoding.Unicode.GetBytes (responseContent));
printers = serializer.ReadObject (ms) as CloudPrinters;
return printers;
catch (Exception)
return printers;
private bool Authorize (out string authCode)
var result = false;
authCode = "";
var queryString = String.Format ("https://www.google.com/accounts/ClientLogin?accountType=HOSTED_OR_GOOGLE&Email=0&Passwd=1&service=cloudprint&source=2",
UserName, Password, Source);
var request = (HttpWebRequest)WebRequest.Create (queryString);
// Setup the web request
SetupWebRequest (request);
var response = (HttpWebResponse)request.GetResponse ();
var responseContent = new StreamReader (response.GetResponseStream ()).ReadToEnd ();
var split = responseContent.Split ('\n');
foreach (var s in split)
var nvsplit = s.Split ('=');
if (nvsplit.Length == 2)
if (nvsplit[0] == "Auth")
authCode = nvsplit[1];
result = true;
return result;
private static void SetupWebRequest (HttpWebRequest webRequest)
// Get the details
var appSettings = ConfigurationManager.AppSettings;
// Create some credentials
if (!String.IsNullOrWhiteSpace (appSettings["ProxyUsername"]))
var cred = new NetworkCredential (appSettings["ProxyUsername"], appSettings["ProxyPassword"],
appSettings["ProxyDomain"]);
// Set the credentials
webRequest.Credentials = cred;
webRequest.Proxy = WebRequest.DefaultWebProxy;
webRequest.Proxy.Credentials = cred;
// Set the timeout
webRequest.Timeout = ServiceTimeout;
webRequest.ServicePoint.ConnectionLeaseTimeout = ServiceTimeout;
webRequest.ServicePoint.MaxIdleTime = ServiceTimeout;
// Turn off the 100's
webRequest.ServicePoint.Expect100Continue = false;
using System.Runtime.Serialization;
namespace GoogleCloudPrintServices.DTO
[DataContract]
public class CloudPrinter
[DataMember (Order = 0)]
public string id get; set;
[DataMember (Order = 1)]
public string name get; set;
[DataMember (Order = 2)]
public string description get; set;
[DataMember (Order = 3)]
public string proxy get; set;
[DataMember (Order = 4)]
public string status get; set;
[DataMember (Order = 5)]
public string capsHash get; set;
[DataMember (Order = 6)]
public string createTime get; set;
[DataMember (Order = 7)]
public string updateTime get; set;
[DataMember (Order = 8)]
public string accessTime get; set;
[DataMember (Order = 9)]
public bool confirmed get; set;
[DataMember (Order = 10)]
public int numberOfDocuments get; set;
[DataMember (Order = 11)]
public int numberOfPages get; set;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace GoogleCloudPrintServices.DTO
[DataContract]
public class CloudPrinters
[DataMember (Order = 0)]
public bool success get; set;
[DataMember (Order = 1)]
public List<CloudPrinter> printers get; set;
using System.Runtime.Serialization;
namespace GoogleCloudPrintServices.DTO
[DataContract]
public class CloudPrintJob
[DataMember (Order = 0)]
public bool success get; set;
[DataMember (Order = 1)]
public string message get; set;
using System;
using System.Collections.Generic;
using System.Text;
namespace GoogleCloudPrintServices.Support
internal class PostData
private const String CRLF = "\r\n";
public string Boundary get; set;
private List<PostDataParam> _mParams;
public List<PostDataParam> Params
get return _mParams;
set _mParams = value;
public PostData ()
// Get boundary, default is --AaB03x
Boundary = "----CloudPrintFormBoundary" + DateTime.UtcNow;
// The set of parameters
_mParams = new List<PostDataParam> ();
public string GetPostData ()
var sb = new StringBuilder ();
foreach (var p in _mParams)
sb.Append ("--" + Boundary).Append (CRLF);
if (p.Type == PostDataParamType.File)
sb.Append (string.Format ("Content-Disposition: form-data; name=\"0\"; filename=\"1\"", p.Name, p.FileName)).Append (CRLF);
sb.Append ("Content-Type: ").Append (p.FileMimeType).Append (CRLF);
sb.Append ("Content-Transfer-Encoding: base64").Append (CRLF);
sb.Append ("").Append (CRLF);
sb.Append (p.Value).Append (CRLF);
else
sb.Append (string.Format ("Content-Disposition: form-data; name=\"0\"", p.Name)).Append (CRLF);
sb.Append ("").Append (CRLF);
sb.Append (p.Value).Append (CRLF);
sb.Append ("--" + Boundary + "--").Append (CRLF);
return sb.ToString ();
public enum PostDataParamType
Field,
File
public class PostDataParam
public string Name get; set;
public string FileName get; set;
public string FileMimeType get; set;
public string Value get; set;
public PostDataParamType Type get; set;
public PostDataParam ()
FileMimeType = "text/plain";
【讨论】:
过去几天我一直在努力解决这个问题……您的回答终于帮助我做对了。让谷歌错误地记录他们的服务...... 如果您设法让 Cloud Print 使用 C# 并可以粘贴一个工作示例,那就太好了。 Deevodavis,我将您移植到 Monodroid 的代码发布在:github.com/slackshot/GoogleCloudPrintMonodroid Xamarin 将做一些示例。我在 MIT 许可下发布了它。如果我可以在这里给你发消息,我会请求许可,但既然你把它贴在这里,我假设你希望人们从中受益。 后续:如何获得可以使用数小时以上的云打印令牌?例如。这里问什么:***.com/questions/15538143/… 你从未给 Source 变量赋值【参考方案2】:对于任何为此苦苦挣扎的人,我创建了一个 github 存储库,其中包含有关如何通过服务帐户使用 Google 云打印的代码和说明,并针对他们的新 OAuth2 身份验证方法进行了更新:
https://github.com/io7/GoogleCloudPrint
【讨论】:
【参考方案3】:看起来问题的原因是您发送到服务器的数据的编码。 在这种情况下,最可靠的解决方案是在发送文档时使用数据 URI 方案。 为此,您需要将 contentType 设置为“dataUrl”并按以下格式传递数据: "data:application/pdf;base64," + Convert.ToBase64String(document)
【讨论】:
我整个晚上都在尝试让云打印在 Java 上运行,这是我唯一错过的事情!谢谢好心的先生!【参考方案4】:需要注意的是,发送capabilities
是强制性的。不发送将导致作业在提交后立即变为错误。应始终发送默认值:
"capabilities":[]
【讨论】:
以上是关于使用 C# 的 Google 云打印的主要内容,如果未能解决你的问题,请参考以下文章