使用Graph API将.NET中的图片发布到Facebook动态墙

5
我正在使用Facebook的JavaScript API开发一个应用程序,需要能够将图片发布到用户的墙上。据我所知,这部分应用程序需要在服务器端完成,因为它需要将图像数据发布为“multipart/form-data”格式。
注意:这不是使用“post”简单版本,而是真正的“photos”方法。

http://graph.facebook.com/me/photos

我认为我面临两个问题,一个是.NET问题,另一个是Facebook问题: Facebook问题:我不确定是否应该将所有参数作为multipart/form-data发送(包括access_token和message)。唯一的代码示例使用了cUrl工具/应用程序。 .NET问题:我从未从.NET发出过multipart/form-data请求,我不确定.NET是否会自动创建mime-parts,或者我是否需要以某种特殊的方式对参数进行编码。
这有点难以调试,因为Graph API仅返回“400 - 错误请求”的错误响应。 以下是我撰写本文时的代码(是的,它有点冗长:-)
当然,最终答案将是从.NET发布图像的示例片段,但我可以接受更少的内容。
string username = null;
string password = null;
int timeout = 5000;
string requestCharset = "UTF-8";
string responseCharset = "UTF-8";
string parameters = "";
string responseContent = "";

string finishedUrl = "https://graph.facebook.com/me/photos";

parameters = "access_token=" + facebookAccessToken + "&message=This+is+an+image";
HttpWebRequest request = null;
request = (HttpWebRequest)WebRequest.Create(finishedUrl);
request.Method = "POST";
request.KeepAlive = false;
//application/x-www-form-urlencoded | multipart/form-data
request.ContentType = "multipart/form-data";
request.Timeout = timeout;
request.AllowAutoRedirect = false;
if (username != null && username != "" && password != null && password != "")
{
    request.PreAuthenticate = true;
    request.Credentials = new NetworkCredential(username, password).GetCredential(new Uri(finishedUrl), "Basic");
}
//write parameters to request body
Stream requestBodyStream = request.GetRequestStream();
Encoding requestParameterEncoding = Encoding.GetEncoding(requestCharset);
byte[] parametersForBody = requestParameterEncoding.GetBytes(parameters);
requestBodyStream.Write(parametersForBody, 0, parametersForBody.Length);
/*
This wont work
byte[] startParm = requestParameterEncoding.GetBytes("&source=");
requestBodyStream.Write(startParm, 0, startParm.Length);
byte[] fileBytes = File.ReadAllBytes(Server.MapPath("images/sample.jpg"));
requestBodyStream.Write( fileBytes, 0, fileBytes.Length );
*/
requestBodyStream.Close();

HttpWebResponse response = null;
Stream receiveStream = null;
StreamReader readStream = null;
Encoding responseEncoding = System.Text.Encoding.GetEncoding(responseCharset);
try 
{
    response = (HttpWebResponse) request.GetResponse();
    receiveStream = response.GetResponseStream();
    readStream = new StreamReader( receiveStream, responseEncoding );
    responseContent = readStream.ReadToEnd();
}
finally 
{
    if (receiveStream != null)
    {
        receiveStream.Close();
    }
    if (readStream != null)
    {
        readStream.Close();
    }
    if (response != null)
    {
        response.Close();
    }
}
5个回答

4

这里是如何上传二进制数据的示例。但是上传到/me/photos将不会发布图片到墙上 :( 图片保存在您的应用程序相册中。我困在如何在动态中宣布它的问题上。另一种方法是通过URL == "graph.facebook.com/%wall-album-id%/photos"将图像发布到“Wall Album”。但没有找到创建此类相册的任何方法(用户在通过该网站上传图像时创建它)。

{
    string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
    uploadRequest = (HttpWebRequest)WebRequest.Create(@"https://graph.facebook.com/me/photos");
    uploadRequest.ServicePoint.Expect100Continue = false;
    uploadRequest.Method = "POST";
    uploadRequest.UserAgent = "Mozilla/4.0 (compatible; Windows NT)";
    uploadRequest.ContentType = "multipart/form-data; boundary=" + boundary;
    uploadRequest.KeepAlive = false;

    StringBuilder sb = new StringBuilder();

    string formdataTemplate = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n";
    sb.AppendFormat(formdataTemplate, boundary, "access_token", PercentEncode(facebookAccessToken));
    sb.AppendFormat(formdataTemplate, boundary, "message", PercentEncode("This is an image"));

    string headerTemplate = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n";
    sb.AppendFormat(headerTemplate, boundary, "source", "file.png", @"application/octet-stream");

    string formString = sb.ToString();
    byte[] formBytes = Encoding.UTF8.GetBytes(formString);
    byte[] trailingBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");

    long imageLength = imageMemoryStream.Length;
    long contentLength = formBytes.Length + imageLength + trailingBytes.Length;
    uploadRequest.ContentLength = contentLength;

    uploadRequest.AllowWriteStreamBuffering = false;
    Stream strm_out = uploadRequest.GetRequestStream();

    strm_out.Write(formBytes, 0, formBytes.Length);

    byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)imageLength))];
    int bytesRead = 0;
    int bytesTotal = 0;
    imageMemoryStream.Seek(0, SeekOrigin.Begin);
    while ((bytesRead = imageMemoryStream.Read(buffer, 0, buffer.Length)) != 0)
    {
        strm_out.Write(buffer, 0, bytesRead); bytesTotal += bytesRead;
        gui.OnUploadProgress(this, (int)(bytesTotal * 100 / imageLength));
    }

    strm_out.Write(trailingBytes, 0, trailingBytes.Length);

    strm_out.Close();

    HttpWebResponse wresp = uploadRequest.GetResponse() as HttpWebResponse;
}

谢谢@fltz。通过调整您的示例(部分变量在片段范围外声明),我已经成功使其运行。 - Torben Warberg Rohde
你的应用程序是将图片发布到墙上还是仅上传到应用程序的相册中?在我的测试中,墙上/动态消息中没有看到已上传的图片。 - fltz
1
我还没有开始处理它。到目前为止,我只在应用程序的默认相册中看到它。但是根据这篇文章的说法,这应该是可能的。 - Torben Warberg Rohde
我在这里回答“如何在自己的时间轴上发布照片”的问题。当一些照片被发布到时间轴上时,它遵循以下两个步骤:
  1. 将照片添加到相册“Timeline_Photos”中。
  2. 发布一个带有照片(上面)ID的新帖子(feed),以便它可以显示为缩略图。
在使用Graph API时,我们也必须遵循相同的步骤:
  1. POST /version/me/photos <提供标题和URL/源>
  2. POST /version/me/feed <提供Object_Attachment,它将是上述步骤中收到的照片ID>
- Risky Pathak

3
使用 @fitz 的代码清理了类方法。传入字节数组或图像文件路径。如果要上传到现有相册,请传入相册 ID。
public string UploadPhoto(string album_id, string message, string filename, Byte[] bytes, string Token)
{
    // Create Boundary
    string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");

    // Create Path
    string Path = @"https://graph.facebook.com/";
    if (!String.IsNullOrEmpty(album_id))
    {
        Path += album_id + "/";
    }
    Path += "photos";

    // Create HttpWebRequest
    HttpWebRequest uploadRequest;
    uploadRequest = (HttpWebRequest)HttpWebRequest.Create(Path);
    uploadRequest.ServicePoint.Expect100Continue = false;
    uploadRequest.Method = "POST";
    uploadRequest.UserAgent = "Mozilla/4.0 (compatible; Windows NT)";
    uploadRequest.ContentType = "multipart/form-data; boundary=" + boundary;
    uploadRequest.KeepAlive = false;

    // New String Builder
    StringBuilder sb = new StringBuilder();

    // Add Form Data
    string formdataTemplate = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n";

    // Access Token
    sb.AppendFormat(formdataTemplate, boundary, "access_token", HttpContext.Current.Server.UrlEncode(Token));

    // Message
    sb.AppendFormat(formdataTemplate, boundary, "message", message);

    // Header
    string headerTemplate = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n";
    sb.AppendFormat(headerTemplate, boundary, "source", filename, @"application/octet-stream");

    // File
    string formString = sb.ToString();
    byte[] formBytes = Encoding.UTF8.GetBytes(formString);
    byte[] trailingBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
    byte[] image;
    if (bytes == null)
    {
        image = File.ReadAllBytes(HttpContext.Current.Server.MapPath(filename));
    }
    else
    {
        image = bytes; 
    }

    // Memory Stream
    MemoryStream imageMemoryStream = new MemoryStream();
    imageMemoryStream.Write(image, 0, image.Length);

    // Set Content Length
    long imageLength = imageMemoryStream.Length;
    long contentLength = formBytes.Length + imageLength + trailingBytes.Length;
    uploadRequest.ContentLength = contentLength;

    // Get Request Stream
    uploadRequest.AllowWriteStreamBuffering = false;
    Stream strm_out = uploadRequest.GetRequestStream();

    // Write to Stream
    strm_out.Write(formBytes, 0, formBytes.Length);
    byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)imageLength))];
    int bytesRead = 0;
    int bytesTotal = 0;
    imageMemoryStream.Seek(0, SeekOrigin.Begin);
    while ((bytesRead = imageMemoryStream.Read(buffer, 0, buffer.Length)) != 0)
    {
        strm_out.Write(buffer, 0, bytesRead); bytesTotal += bytesRead;
    }
    strm_out.Write(trailingBytes, 0, trailingBytes.Length);

    // Close Stream
    strm_out.Close();

    // Get Web Response
    HttpWebResponse response = uploadRequest.GetResponse() as HttpWebResponse;

    // Create Stream Reader
    StreamReader reader = new StreamReader(response.GetResponseStream());

    // Return
    return reader.ReadToEnd();
}

1

我能够使用RestSharp发布图片:

// url example: https://graph.facebook.com/you/photos?access_token=YOUR_TOKEN
request.AddFile("source", imageAsByteArray, openFileDialog1.SafeFileName, getMimeType(Path.GetExtension(openFileDialog1.FileName)));
request.addParameter("message", "your photos text here");

用户API页面API用于发布照片

如何将图像转换为字节数组

注意:我将空字符串作为MIME类型传递,Facebook足够聪明以找出正确的类型。


1

你必须使用字节数组自己构建multipart/form-data。 无论如何,我已经做到了。你可以在http://computerbeacon.net/查看Facebook Graph Toolkit。我将在几天内更新工具包至0.8版本,其中将包括“发布照片到Facebook墙”的功能以及其他新功能和更新。


谢谢 - 期待试用。 - Torben Warberg Rohde
该网站无法访问。 - zchpit

0

可能有用

        [TestMethod]
        [DeploymentItem(@".\resources\velas_navidad.gif", @".\")]
        public void Post_to_photos()
        {
            var ImagePath = "velas_navidad.gif";
            Assert.IsTrue(File.Exists(ImagePath));

            var client = new FacebookClient(AccessToken);
            dynamic parameters = new ExpandoObject();

            parameters.message = "Picture_Caption";
            parameters.subject = "test 7979";
            parameters.source = new FacebookMediaObject
{
    ContentType = "image/gif",
    FileName = Path.GetFileName(ImagePath)
}.SetValue(File.ReadAllBytes(ImagePath));

            //// Post the image/picture to User wall
            dynamic result = client.Post("me/photos", parameters);
            //// Post the image/picture to the Page's Wall Photo album
            //fb.Post("/368396933231381/", parameters); //368396933231381 is Album id for that page.

            Thread.Sleep(15000);
            client.Delete(result.id);
        }

参考: 发出请求

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接