Google Cloud Print с использованием C # - PullRequest
7 голосов
/ 21 октября 2011

Я пытаюсь использовать Google Cloud Print с использованием 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 };
        }
    }

Я запускаю этот код, тогда есть интерфейс моего принтера, но печать не происходит.Интерфейс моего принтера говорит, что страниц для печати 0, а размер файла не совпадает с тем, который я отправил на принтер.

Google Cloud Print говорит, что задача (задание) успешно добавлена, нов интерфейсе Google Cloud Print рядом с именем документа отображается «Ошибка».

Я подумал, что может возникнуть проблема с HttpUtility.UrlEncode или Convert.ToBase64String , но я попробовал обратное преобразование - все работает.

У кого-нибудь есть идеи?

Ответы [ 4 ]

7 голосов
/ 19 августа 2015

Для тех, кто борется с этим, я создал хранилище github с кодом и инструкциями по использованию облачной печати Google с учетной записью службы, обновленной для их нового метода аутентификации OAuth2:

https://github.com/io7/GoogleCloudPrint

7 голосов
/ 17 апреля 2012

Я ценю, что этот вопрос уже немного устарел, но недавно мне пришлось посмотреть на это из-за того, что я делаю на работе, и хотя опубликованный пример кода направил меня в правильном направлении, он действительно взял меня довольно некоторое время, чтобы заставить его работать полностью.

Функция «список принтеров» работала нормально, как описано, но я не мог заставить отправку работать должным образом, она просто перешла к ошибке, как описывает OP.

После профилирования фактического запроса на отправку в Chrome с использованием Fiddler и просмотра примера кода PHP на веб-сайте Google, я обнаружил, что отправка должна быть составным MIME-сообщением, иначе оно не будет работать должным образом.

Это пример сообщения HTTP POST, которое, наконец, начало работать с операцией Submit. Обратите внимание, что идентификатор принтера gcp должен быть передан в 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--

Меня смутило то, что значение Boundary, указанное в заголовке, имеет на 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";
        }
    }
}
3 голосов
/ 27 октября 2011

Похоже, что причиной проблемы является кодирование данных, отправленных на сервер. Наиболее надежным решением в этом случае будет использование схемы URI данных при отправке документа. Для этого вам нужно установить для contentType значение «dataUrl» и передать данные в следующем формате: "data: application / pdf; base64," + Convert.ToBase64String (document)

0 голосов
/ 10 октября 2012

Следует отметить, что отправка capabilities является обязательной. Отказ от отправки приведет к тому, что задание переключится на Ошибка сразу после отправки. Всегда следует отправлять значение по умолчанию:

{"capabilities":[{}]}
...