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