2011-10-21 14 views
5

Próbuję korzystać z Google Cloud Print przy użyciu C#. Internet ma tylko jeden example, który napisał Josh Goebel. nie opublikuje kompletny przykład, tutaj jest to jedyna metoda, która wysyła plik do druku:Google Cloud Print przy użyciu C#

 

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 }; 
     } 
    } 
 

ten kod, to nie jest to interfejs z mojej drukarki, lecz druku się nie dzieje. Interfejs mojej drukarki mówi, że strony do wydrukowania 0 oraz rozmiar pliku nie pokrywają się z tym, który wysłałem do drukarki.

Google Cloud Print mówi, że zadanie (zadanie) zostało pomyślnie dodane, ale w interfejsie Google Cloud Print obok nazwy dokumentu wyświetla się komunikat "Błąd".

Myślałem, że może mieć problem z HttpUtility.UrlEncode lub Convert.ToBase64String, ale starałem odwrotną transformację - wszystko działa.

Czy ktoś ma jakieś pomysły?

+0

pisałem Josh Goebel poprzez Twitter - powiedział czego nie napisałem ten przykład. Hej, ludzie, którzy znają tego programistę, który to napisał? :) – t0rch

+1

To był długi czas, więc zapomniałem odpowiedzi :) Ale patrzę na mój kod i to, co widzę (po prostu porównaj z górnym kodem): "& contentType =" + HttpUtility.UrlEncode ("url") + " & content = "+ HttpUtility.UrlEncode (treść); Usuwam również cały kod po odpowiedzi łańcuchowejContent = new StreamReader (response.GetResponseStream()). ReadToEnd(); – t0rch

Odpowiedz

2

Wygląda na to, że przyczyną problemu jest kodowanie danych wysłanych na serwer. Najbardziej niezawodnym rozwiązaniem w tym przypadku byłoby użycie schematu URI danych podczas wysyłania dokumentu. Aby to zrobić trzeba ustawić contentType do "dataUrl" i przekazać dane w następującym formacie: "Dane: application/pdf; base64," + Convert.ToBase64String (dokument)

+0

Spędziłem cały wieczór próbując drukować w chmurze na Javie i to jedyna rzecz, którą przegapiłem! Uprzejmie panu dziękuję! – Sver

6

Doceniam to pytanie jest trochę już stary, ale ostatnio musiałem spojrzeć na to za coś, co robię w pracy, i chociaż napisany przeze mnie kod wyprowadził mnie we właściwym kierunku, zajęło mi to sporo czasu, aby całkowicie zadziałało.

Funkcja "listy drukarek" działała poprawnie zgodnie z opisem, ale nie udało mi się poprawnie wysłać przesyłki, po prostu od razu trafił na błąd opisany w OP.

Po wyprofilowaniu faktycznego zgłoszenia w Chrome przy użyciu aplikacji Fiddler i przejrzeniu kodu PHP w witrynie Google odkryłem, że zgłoszenie musi być wieloczęściową wiadomością MIME lub nie działałoby poprawnie.

To jest przykład komunikatu HTTP POST, który zakończył działanie operacji przesyłania. Należy zauważyć, że identyfikator drukarki gcp musi zostać przekazany w adresie URL żądania, a dane (niezależnie od tego, czy są to pliki PDF, JPG lub PNG), muszą być zakodowane w Base64 i prawidłowy zestaw typów mime (application/pdf, image/jpeg .. .) dla bloku „content” (ten ostatni na liście poniżej):

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-- 

miejsca, która wyłączyła mnie było to, że wartości brzegowych określony w nagłówku ma 2 mniej myślniki (-) niż rzeczywista ich użycie (nie jest oczywiste, gdy patrzysz na coś zastanawiającego się, dlaczego to nie działa!), ostatnia instancja graniczna ma na końcu dwa dodatkowe łączniki i że muszę pozbyć się nagłówka C# Expect100Continue (używając żądania .ServicePoint.Expect100Continue = false).

AKTUALIZACJA: Oto pełny kod, który napisałem w tym czasie, na wypadek, gdyby pomógł komukolwiek.

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"; 
     } 
    } 
} 
+0

Pracuję nad tym przez ostatnie kilka dni ... Twoja odpowiedź w końcu pomogła mi to naprawić. Zostaw to Google, aby nieprawidłowo udokumentować swoje usługi ... – Justin

+1

Jeśli udało ci się uzyskać Cloud Print działający z C# i mógłbyś wkleić działający przykład, byłoby wspaniale. – Craig

+0

Deevodavis, napisałem twój kod przeniesiony do Monodroid na: https://github.com/slackshot/GoogleCloudPrintMonodroid Xamarin zrobi kilka przykładów. Wydałem go na podstawie licencji MIT. Gdybym mógł cię tu przesłać, musiałbym poprosić o pozwolenie, ale skoro już to opublikowałeś, zakładam, że chciałeś, aby ludzie z niego skorzystali. –

0

Należy zauważyć, że wysyłanie capabilities jest obowiązkowe. Nie wysłanie go spowoduje, że zlecenie zmieni się na Błąd natychmiast po przesłaniu.Wartość domyślna powinna zawsze być wysyłane:

{"capabilities":[{}]} 
5

Dla każdego, kto zmaga się z tym, że stworzyliśmy repozytorium GitHub z kodem i instrukcje na temat korzystania z Cloud Print Google z kontem usługi, aktualizowanej na ich nowej metody uwierzytelniania OAuth2 :

https://github.com/io7/GoogleCloudPrint

Powiązane problemy