使用C#的Google云打印

我尝试使用C#使用Google云打印。 互联网只是一个例子 ,他写了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,文件大小与我发送到打印机的页面大小不一致。

Google Cloud Print表示已成功添加任务(作业),但在文档名称旁边的Google Cloud Print界面显示“错误”。

我认为HttpUtility.UrlEncodeConvert.ToBase64String可能存在问题,但我尝试了反向转换 – 一切正常。

有没有人有任何想法?

我很欣赏这个问题现在有点老了,但是我最近不得不看一下这个我在工作中正在做的事情,虽然发布的示例代码让我朝着正确的方向开始,但我确实花了很长时间才让它完全运作。

“列表打印机”function正常工作,如上所述,但我无法正常工作,它只是直接到OP描述的错误。

在使用Fiddler在Chrome中分析实际提交请求并查看google网站上的PHP示例代码后,我发现提交需要是多部分MIME消息,否则无法正常运行。

这是HTTP POST消息的一个示例,它最终使Submit操作正常工作。 请注意,需要在请求URL中传递gcp Printer Id,并且数据(无论是PDF,JPG还是PNG)需要进行Base64编码并设置正确的mime类型(application / pdf,image / jpeg .. 。)对于“内容”块(下面列表中的最后一个):

 POST http://www.google.com/cloudprint/submit?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 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 _mParams; public List 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 (); } 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"; } } } 

对于那些正在努力解决这个问题的人,我创建了一个github存储库,其中包含有关如何将Google云打印与服务帐户结合使用的代码和说明,并针对其新的OAuth2身份validation方法进行了更新:

https://github.com/io7/GoogleCloudPrint

看起来问题的原因是您发送到服务器的数据的编码。 在这种情况下,最可靠的解决方案是在发送文档时使用数据URI方案。 为此,您需要将contentType设置为“dataUrl”并以下列格式传递数据:“data:application / pdf; base64,”+ Convert.ToBase64String(document)

应该注意,发送capabilities是强制性的。 不发送将导致作业在提交后立即转为错误 。 应始终发送默认值:

 {"capabilities":[{}]}