关于sina api上传图片接口(upload) c#版

陈着 posted @ 14 年前 in Other , 8457 阅读

    之前我写了sinaapi c#的sdk:opensinaapi,没有实现上传图片的接口。因为要忙实验室和其他项目,一直没有去研究。期间很多人询问过我有关上传图片接口的问题,最近刚好有些时间,花了一个晚上实现出来,需要的同学可以去http://code.google.com/p/opensinaapi/downloads/list下载。

    在研究的过程中我发现相关的资料非常少,基本上不能找到有用的信息。相对来说,twitter api的upload借口实现有其借鉴意义,但是其并不适用在sina api上,如果有人去研究的话应该会发现大多会返回401错误(未能授权)。原因是:sina api和twitter api中post的参数不一样,sina api中多了一个参数:source,少了oauth_verify。POST的参数和Update的接口一样,需要将status加入签名。

    我在研究过程中主要是抓取upload的POST包,对包结构进行分析,从而用c#构造POST包(抓包工具HttpAnalyzer)。以下为一个真实的POST包:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
POST /statuses/upload.json HTTP/1.1
Authorization: OAuth oauth_consumer_key="3587646579", oauth_signature_method="HMAC-SHA1",oauth_timestamp="1289113593", oauth_nonce="7472747", oauth_version="1.0", oauth_token="9f273ceccf51e528b302a3a47053badd",oauth_signature="O5b4XTaazF6T50YqSsyLM6RtRsk%3d"
User-Agent: Jakarta Commons-HttpClient/3.1
Content-Type: multipart/form-data; boundary=bb9f588b-0eaf-4d95-aaec-b10f057bccd7
Host: api.t.sina.com.cn
Content-Length: 26509
 
--bb9f588b-0eaf-4d95-aaec-b10f057bccd7
Content-Disposition: form-data; name="status"
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 8bit
 
%e6%88%91%e6%98%af%e9%99%88%e7%9d%80
--bb9f588b-0eaf-4d95-aaec-b10f057bccd7
Content-Disposition: form-data; name="source"
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 8bit
 
3587646579
--bb9f588b-0eaf-4d95-aaec-b10f057bccd7
Content-Disposition: form-data; name="pic"; filename="sample_image.png"
Content-Type: application/octet-stream; charset=UTF-8
Content-Transfer-Encoding: binary
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
--bb9f588b-0eaf-4d95-aaec-b10f057bccd7


XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX为二进制文件串

以下是构造POST包的c#代码

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
public string oAuthWebRequestWithPic(Method method, string url, string postData,string filepath,string picName)
        {
            var UploadApiUrl = url;
            string status = postData.Split('=').GetValue(1).ToString();
            postData += "&source=" + _consumerKey;//多了一个source参数
 
            if (postData.Length > 0)
            {
                NameValueCollection qs = HttpUtility.ParseQueryString(postData);
                postData = "";
                foreach (string key in qs.AllKeys)
                {
                    if (postData.Length > 0)
                    {
                        postData += "&";
                    }
                    qs[key] = HttpUtility.UrlEncode(qs[key]);
                    qs[key] = this.UrlEncode(qs[key]);
                    postData += (key + "=" + qs[key]);
 
                }
                if (url.IndexOf("?") > 0)
                {
                    url += "&";
                }
                else
                {
                    url += "?";
                }
                url += postData;
            }
 
            var oauthSignaturePattern = "OAuth oauth_consumer_key=\"{0}\", oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"{1}\", oauth_nonce=\"{2}\", oauth_version=\"1.0\", oauth_token=\"{3}\",oauth_signature=\"{4}\"";
             
            var contentEncoding = "iso-8859-1";
 
            string normalizedString, normalizedParameters;
            var timestamp = this.GenerateTimeStamp();
            var nounce = this.GenerateNonce();
 
            var signature = this.GenerateSignature(
                                new Uri(url),
                                this.ConsumerKey,
                                this.ConsumerSecret,
                                this.Token,
                                this.TokenSecret,
                                method.ToString(),
                                timestamp,
                                nounce,
                                out normalizedString,
                                out normalizedParameters);
            signature = HttpUtility.UrlEncode(signature);
 
            var boundary = Guid.NewGuid().ToString();
            var request = (HttpWebRequest)System.Net.WebRequest.Create(UploadApiUrl);
 
            request.PreAuthenticate = true;
            request.AllowWriteStreamBuffering = true;
            request.Method = method.ToString();
            request.UserAgent = "Jakarta Commons-HttpClient/3.1";
            var authorizationHeader = string.Format(
                                        CultureInfo.InvariantCulture,
                                        oauthSignaturePattern,
                                        this.ConsumerKey,
                                        timestamp,
                                        nounce,
                                        this.Token,
                                        signature);
            request.Headers.Add("Authorization", authorizationHeader);
 
            var header = string.Format("--{0}", boundary);
            var footer = string.Format("--{0}--", boundary);
 
            var contents = new StringBuilder();
            request.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);
            contents.AppendLine(header);
            contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "status"));
            contents.AppendLine("Content-Type: text/plain; charset=US-ASCII");
            contents.AppendLine("Content-Transfer-Encoding: 8bit");
            contents.AppendLine();
            contents.AppendLine(status);
 
            contents.AppendLine(header);
            contents.AppendLine(string.Format("Content-Disposition: form-data; name=\"{0}\"", "source"));
            contents.AppendLine("Content-Type: text/plain; charset=US-ASCII");
            contents.AppendLine("Content-Transfer-Encoding: 8bit");
            contents.AppendLine();
            contents.AppendLine(this.ConsumerKey);
 
 
            contents.AppendLine(header);
            string fileHeader = string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"", "pic", picName);
            string fileData = System.Text.Encoding.GetEncoding(contentEncoding).GetString(File.ReadAllBytes(@filepath));
 
            contents.AppendLine(fileHeader);
            contents.AppendLine("Content-Type: application/octet-stream; charset=UTF-8");
            contents.AppendLine("Content-Transfer-Encoding: binary");
            contents.AppendLine();
            contents.AppendLine(fileData);
            contents.AppendLine(footer);
 
            byte[] bytes = Encoding.GetEncoding(contentEncoding).GetBytes(contents.ToString());
            request.ContentLength = bytes.Length;
             
            var requestStream = request.GetRequestStream();          
            try
            {
                requestStream.Write(bytes, 0, bytes.Length);
            }
            catch
            {
                throw;
            }
            finally
            {
                requestStream.Close();
                requestStream = null;
            }
            return WebResponseGet(request);
        }

其次,需要修改oAuthBase.cs,这是因为签名参数的问题:多了source少了oauth_verify

 

1
2
3
4
5
6
public string GenerateSignatureBase(Uri url, string consumerKey, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, string signatureType, out string normalizedUrl, out string normalizedRequestParameters)中
 
if (!string.IsNullOrEmpty(oauth_verifier))
改为
if (!string.IsNullOrEmpty(oauth_verifier) && httpMethod == "GET")

有个比较奇怪的问题,sina api的update接口可以不需要将oauth_verify换为source,而upload接口则必须将oauth_verify换为source。

最后,本版本的sdk中,upload的status必须为英文,暂时还没解决中文问题。

注:由于本人时间有限,而sdk还相当不完善,如有同学想加入opensinaapi,请联系sarlmolapple@gmail.com,不胜荣幸,希望能帮助更多的人。

Avatar_small
steven 说:
14 年前

你吧status这里的一段中的charset=US-ASCII改为charset=UTF-8貌似就能支持中文了。

Avatar_small
陈着 说:
14 年前

@steven: 貌似不改也是可以发中文的,当时写博客的时候灵异了。。。

Avatar_small
steven 说:
14 年前

你提供的方法没有添加地理坐标信息,我插入2段后,竟然提示401错误,不知道错在那里

//contents.AppendLine(header);
//contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "lat"));
//contents.AppendLine("Content-Type: text/plain; charset=UTF-8");
//contents.AppendLine("Content-Transfer-Encoding: 8bit");
//contents.AppendLine();
//contents.AppendLine(lat.ToString());

//contents.AppendLine(header);
//contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "long"));
//contents.AppendLine("Content-Type: text/plain; charset=UTF-8");
//contents.AppendLine("Content-Transfer-Encoding: 8bit");
//contents.AppendLine();
//contents.AppendLine(longitude.ToString());

Avatar_small
陈着 说:
14 年前

下个版本会解决这个问题。


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter