weibo.emacs
weibo.emacs copied to clipboard
如何发表带图片的原创微博
按P打开编辑新微博的小窗口以后,只能输入文本,以后是否准备支持插入图片?
@naihe2010 你好,谢谢你的建议。暂时还不能发布图片,以后可以加入支持。
@austin----- 发歹带图片的API在 http://open.weibo.com/wiki/2/statuses/upload ,参数中有个pic
pic true binary 要上传的图片,仅支持JPEG、GIF、PNG格式,图片大小小于5M。
其类型是 binary,这个参数应该如何处理(1、ELisp 如何读图片文件;2、如何设置url-request-data
)?
并且还要求使用 multipart/form-data
编码方式,是不是就是把 Content-Type
设置成 multipart/form-data
?
目前相关的实现如下(这里的参数都是 string 类型的):
(defun weibo-send-url (url args)
(let ((url-request-method "POST")
(url-request-extra-headers
`(("Content-Type" . "application/x-www-form-urlencoded")
("Authorization" . ,(format "OAuth2 %s" (url-hexify-string (weibo-get-token))))))
(url-request-data
(mapconcat (lambda (arg)
(concat (url-hexify-string (car arg))
"="
(url-hexify-string (cdr arg))))
args
"&")))
(cl-flet ((message (&rest args) nil))
(url-retrieve-synchronously url))))
(with-temp-buffer (insert-file-content "path") (buffer-substring-no-properties (point-min) (point-max))) 可以得到文件内容
实现的时候需要构建一个multipart/form-data的请求。如果没有现成的elisp函数可用的话,需要了解一下multipart的格式。这里有详细的说明:http://tinyurl.com/2e45t2n
我看了你给的链接,其中给的一个示例(user agent 应该发给 server 的数据):
Content-Type: multipart/form-data; boundary=AaB03x
--AaB03x
Content-Disposition: form-data; name="submit-name"
Larry
--AaB03x
Content-Disposition: form-data; name="files"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
(1) 但是这里有多个 Content-Type
,不知道怎么构造?
(2) 我的失败的尝试如下,是不是应该把 binary (pic
)和 string (status
)都塞到 url-request-data
?
(let ((url-request-method "POST")
(url-request-extra-headers
`(("Content-Type" . "multipart/form-data; boundary=AaB03x") ; <== (1)
("Authorization" . ,(format
"OAuth2 %s"
(url-hexify-string (weibo-get-token))))))
(url-request-data
(url-hexify-string
(concat "status=a sample tweet"
"&"
"pic=" img-binary)))) ; <== (2)
(cl-flet ((message (&rest args) nil))
(url-retrieve-synchronously
"https://upload.api.weibo.com/2/statuses/upload.json")))
(setq img-binary
(with-temp-buffer
(insert-file-literally "~/Desktop/a.jpeg")
(buffer-substring-no-properties (point-min)
(point-max))))
multipart的意思就是,每一个query的参数都是一个独立的content-disposition。一般参数就参照例子中submit-name去构造。文件(binary)参照files的例子构造,区别是在这里的话,binary的名字应该是pic,type是image/jpeg,image/png或者image/gif。然后img-binary就直接concat到这个disposition里面。
建议可以先构造好request,然后用fiddler之类的工具先试试是否可以被weibo接受。request应该类似于这样:
Content-Type: multipart/form-data; boundary=AaB03x
--AaB03x
Content-Disposition: form-data; name="status"
a sample tweet
--AaB03x
Content-Disposition: form-data; name="files"; filename="file_to_upload.jpg"
Content-Type: image/jpeg
... contents of <img-binary> ...
--AaB03x--
还是不知道如何构造(url-request-extra-headers
和url-request-data
应该怎么填充),不清楚有没有人能帮忙?
用 curl 很容易模拟这个请求:
➜ ~ curl -X POST -H "Authorization: _Token_" \
> -F "status=Sample tweet with picture." \
> -F "pic=@/users/xcy/Desktop/a.jpeg" \
> https://api.weibo.com/2/statuses/upload.json
用wireshark抓一下包,就能看到curl发送的request了。
一次成功的 HTTP 请求是这样的:
POST /2/statuses/upload.json HTTP/1.1
Host: api.weibo.com
Authorization: OAuth2 2.00GnoVkCoL1sjD1772f1d78awzXvWE
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="status"
Sample tweet with picture
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="pic"; filename="a.jpg"
Content-Type: image/jpeg
----WebKitFormBoundaryE19zNvXGzXaLvS5C
从 Chrome Developer Tools 拷贝下来的完整 curl 指令:
curl 'https://api.weibo.com/2/statuses/upload.json' -H 'origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm' -H 'accept-encoding: gzip, deflate' -H 'accept-language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4,zh-TW;q=0.2' -H 'authorization: OAuth2 2.00GnoVkCoL1sjD1772f1d78awzXvWE' -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundaryUhF5gxr7J2MvOooO' -H 'accept: */*' -H 'cache-control: no-cache' -H 'cookie: SINAGLOBAL=7074588378891.349.1404558680176; __utma=15428400.471598242.1423718540.1423718540.1423718540.1; __utmz=15428400.1423718540.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); myuid=2519560532; wvr=6; _s_tentry=login.sina.com.cn; Apache=9431151158642.023.1427114663568; ULV=1427114664336:165:38:5:9431151158642.023.1427114663568:1427106607630; SUS=SID-2519560532-1427205957-GZ-74zji-26f5fd39b461f21a66f5b6803b7c841f; SUE=es%3D0a000cff1f95041bb7d7ce91f4951916%26ev%3Dv1%26es2%3Ded4ef021ad50d11fea1ed5057961f9ca%26rs0%3Di1ZUtqBy4Br1BAo00MqwUfI%252BmuNIpxZSf6jc%252BQD7bZEzXw94iGCxtmK34Yt%252BiksucyZ1siMcuW%252FQSn7q8z4szEzQIGnNsYYgRU5ObHSRx3X%252Bv4mKoiEWd4mpwWYeBkfMk1yyhlPHsZ%252BdevWvP47kM1KOe37eVi%252F0wxRuc80xWEQ%253D%26rv%3D0; SUP=cv%3D1%26bt%3D1427205957%26et%3D1427292357%26d%3Dc909%26i%3D841f%26us%3D1%26vf%3D0%26vt%3D0%26ac%3D2%26st%3D0%26uid%3D2519560532%26name%3Dxuchunyang56%2540gmail.com%26nick%3Dskin%26fmp%3D%26lcp%3D; SUB=_2A254FR8VDeTxGeRL6lsU9i7JyD6IHXVbY3fdrDV8PUNbvtBeLUHtkW8yLF1Zsyy6Rct073FRgXUWX_HrvA..; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9W5gC63qiNlFxcfBGDdL7Ane5JpX5KMt; SUHB=0QS4KHgr_32aeY; ALF=1458741957; SSOLoginState=1427205957; UOR=www.infzm.com,widget.weibo.com,www.infzm.com' -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36' --data-binary $'------WebKitFormBoundaryUhF5gxr7J2MvOooO\r\nContent-Disposition: form-data; name="status"\r\n\r\nSample tweet with picture\r\n------WebKitFormBoundaryUhF5gxr7J2MvOooO\r\nContent-Disposition: form-data; name="pic"; filename="a.jpg"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundaryUhF5gxr7J2MvOooO--\r\n' --compressed
我的失败的尝试(估计是url-request-data
有问题):
(setq image-data
(with-temp-buffer
(insert-file-contents-literally "~/Desktop/a.jpg")
(buffer-substring-no-properties (point-min)
(point-max))))
(let ((url-request-method "POST")
(url-request-extra-headers
`(("Content-Type" .
"multipart/form-data; boundary=---")
("Authorization" .
,(format "OAuth2 %s" (url-hexify-string (weibo-get-token))))))
(url-request-data
(concat "---\r\nContent-Disposition: form-data; name=\"status\"\r\n\r\n"
"This is a test\r\n"
"---\r\n"
"Content-Disposition: form-data; name=\"pic\"; filename=\"a.jpg\"\r\n"
"Content-Type: image/jpeg\r\n\r\n"
image-data
"\r\n"
"---")))
(url-retrieve-synchronously "https://api.weibo.com/2/statuses/upload.json"))
如果你知道如何操作,请直接把 elisp 代码贴出来。
boundary可能有问题: 定义boundary的时候:multipart/form-data; boundary= 这里的string可以为一个长一些的random字符串。 在request-data中使用的时候,还要在这个字符串前再加两个"-"字符。最后一个boundary还要在最后额外加两个"-":
(setq image-data
(with-temp-buffer
(insert-file-contents-literally "~/Desktop/a.jpg")
(buffer-substring-no-properties (point-min)
(point-max))))
(let ((url-request-method "POST")
(url-request-extra-headers
`(("Content-Type" .
"multipart/form-data; boundary=--WebKitFormBoundaryE19zNvXGzXaLvS5C")
("Authorization" .
,(format "OAuth2 %s" (url-hexify-string (weibo-get-token))))))
(url-request-data
(concat "----WebKitFormBoundaryE19zNvXGzXaLvS5C\r\nContent-Disposition: form-data; name=\"status\"\r\n\r\n"
"This is a test\r\n"
"----WebKitFormBoundaryE19zNvXGzXaLvS5C\r\n"
"Content-Disposition: form-data; name=\"pic\"; filename=\"a.jpg\"\r\n"
"Content-Type: image/jpeg\r\n\r\n"
image-data
"\r\n"
"----WebKitFormBoundaryE19zNvXGzXaLvS5C--")))
(url-retrieve-synchronously "https://api.weibo.com/2/statuses/upload.json"))