2011-11-07 143 views
52

以下是對所有mathematica標記追隨者的挑戰。讓我們通過創建一個imgur上傳器,將圖像插入到Mathematica的SO帖子中變得更加方便。將圖像從Mathematica上傳到Imgur

我們怎樣才能創建一個函數imgur[g_],將光柵化它的參數(確保最終的尺寸不大於StackOverflow的職位的寬度更寬),將其轉換成PNG,上傳到imgur,並返回一個隨時可以粘貼MarkDown行如![Mathematica graphic](http://i.imgur.com/ZENa4.jpg)

有用的參考資料:

我失敗,沒有首先導出到文件,以適應後者的方法來上傳圖片。


警告,小心使用! StackOverflow使用separate imgur installation,可以無限期地保留圖像。如果你使用主視圖,the images will disappear after 6 months if no one views them。不幸的是,截至2011年11月,似乎有no official way以編程方式將圖像上傳到StackOverflow。


更新:See below上傳到計算器直接的溶液。

+2

請注意,上傳到'HTTP:// i.stack.imgur.com /'是比較困難的(你要「驅動」 SO接口) –

+0

@belisarius哎呀,我沒有意識到的StackOverflow使用一個單獨的imgur網站...在主imgur網站上的圖像可能不會永久保存,所以也許它不是一個好主意,用於SO http://imgur.com/faq#long(至少1個視圖/需要6個月才能保存) – Szabolcs

+4

手動發佈圖形並不是很難(在V8中)。右鍵單擊圖形,選擇「圖像另存爲...」。然後,文件對話框將打開上次的位置,通常是我的桌面,其中名爲output.png的文件已經在等待其下一個版本被覆蓋。只需點擊兩次,再點擊兩次,並將其發佈在我的SO回答框中。最多需要15秒。獲取Markdown行並粘貼將花費大約相同的時間。 –

回答

16

一隻小鳥只是告訴我一個數學解決的這個問題(底層實現仍然使用JLINK,但這個答案隱藏所有的與Java相關的代碼):

imgur[expr_] := Module[ 
{url, key, image, data, xml, imgurUrl}, 
url = "http://api.imgur.com/2/upload"; 
key = "c07bc3fb59ef878d5e23a0c4972fbb29"; 
image = Fold[ExportString, expr, {"PNG", "Base64"}]; 
xml = Import[url, 
    "XML", "RequestMethod" -> "POST", 
    "RequestParameters" -> {"key" -> key, "image" -> image}]; 
imgurUrl = Cases[xml, XMLElement["original", {}, {string_}] :> string, 
    Infinity][[1]]; 
"![Mathematica graphic](" <> imgurUrl <> ")" 
] 

這是V8僅XML進口選項"RequestMethod""RequestParameters"沒有記錄和實驗(因此可能會有變化)。

+0

謝謝你分享這個Arnoud!不幸的是,當嘗試上傳到StackOverflow時(使用我在其他答案的評論中提到的方法),這似乎不起作用。我認爲問題在於圖像必須以[multiplart/form-data](http://en.wikipedia.org/wiki/Multipart/form-data#Form_Data)的形式提交。你知道該怎麼做嗎?另外,你有什麼想法如何使它成爲一個調色板按鈕,將上傳選擇(預覽後)?查看我的其他答案,瞭解我嘗試過的方法以及它的工作方式(太窄的調色板寬度用於光柵化)。 – Szabolcs

+0

+1:非常好!現在我試圖讓它與OAuth一起工作,以便我可以直接將圖像上傳到我的帳戶相冊等。你是怎麼找到這些無證的選項的? – nixeagle

+0

@nixeagle如果你得到這個工作,我不會介意你是否在這裏發佈了代碼(解釋挑戰以及如何解決它們)作爲附加答案。 – Szabolcs

12

注意:這是使用匿名密鑰的匿名imgur上傳。 imgur網站將上傳限制爲50次上傳/小時,這應該是正常的,但如果很多人同時嘗試,這可能會導致問題。所以,請在這裏獲得自己的匿名鍵:

http://imgur.com/register/api_anon

,然後替換在下面用自己的密鑰的代碼鍵(感謝!)。

編碼最棘手的部分是從Mathematica表達式到PNG圖像到Base64編碼到URL編碼的轉換。有大約1000種方式做錯了,我想我設法嘗試所有。

代碼分解成幾塊:

  • 構建POST網址
  • 使HTTP連接
  • 發送POST網址
  • 讀回的結果,這是XML
  • 從XML中提取imgur url
  • 將imgur url格式化爲markdown(或作爲MathematicaHyperlink函數)。

下面是代碼:

imgur[expr_] := 
Module[{url, key, image, data, jUrl, jConn, jWriter, jInput, buffer, 
    byte, xml, imgurUrl}, 
    Needs["JLink`"]; 
    JLink`JavaBlock[ 
    JLink`LoadJavaClass["java.net.URLEncoder"]; 
    url = "http://api.imgur.com/2/upload"; 
    key = "c07bc3fb59ef878d5e23a0c4972fbb29"; 
    image = ExportString[ExportString[expr, "PNG"], "Base64"]; 
    data = 
    URLEncoder`encode["key" , "UTF-8"] <> "=" <> 
    URLEncoder`encode[ key , "UTF-8"] <> "&" <> 
    URLEncoder`encode["image" , "UTF-8"] <> "=" <> 
    URLEncoder`encode[ image , "UTF-8"] ; 
    jUrl = JLink`JavaNew["java.net.URL", url]; 
    jConn = [email protected][]; 
    [email protected][True]; 
    jWriter = 
    JLink`JavaNew["java.io.OutputStreamWriter", 
    [email protected][]]; 
    [email protected][data]; 
    [email protected][]; 
    jInput = [email protected][]; 
    buffer = {}; 
    While[(byte = [email protected][]; byte >= 0), AppendTo[buffer, byte]]; 
    ]; 
    xml = ImportString[FromCharacterCode[buffer], "XML"]; 
    imgurUrl = 
    Cases[xml, 
    XMLElement["original", {}, {string_}] :> 
     string, \[Infinity]][[1]]; 
    "![Mathematica graphic](" <> imgurUrl <> ")" 
    ] 

測試:

In[]:= g = Graphics[{Blue, Disk[]}, PlotRange -> 1.2, ImageSize -> Small]; 
     pic = Overlay[{Blur[[email protected], 10], g}]; 
     imgur[pic] 

Out[]= ![Mathematica graphic](http://i.imgur.com/eGOlL.png) 

和實際圖像:

Mathematica graphic

+0

有沒有辦法做編碼和在Mathematica本身中上傳,而不訴諸'JLink'? +1,順便說一句。 – rcollyer

+0

@rcollyer - 據我所知,不在V8中。編碼和上傳您希望作爲Mathematica的內置功能看到的內容? –

+1

我得到的錯誤:''的Java :: excptn:發生Java異常:java.io.IOException異常:服務器返回的HTTP響應代碼:400網址:http://api.imgur.com/2/upload \t在陽光下.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1290)。 XML'Parser'XMLGetString ::字符串:字符串中XML'Parser'XMLGetString [EndOfFile] .'' –

13

注:獲取一個讀y-made調色板,具有此功能here


阿諾德的解決方案讓我興奮和急躁,所以這裏有一個改進。如果不研究他的代碼,我無法做到這一點。這個版本似乎更可靠,不太容易出現超時錯誤,但說實話,我根本不知道Java,所以任何改進都是值得歡迎的。

最重要的是:這個版本直接上傳到stack.imgur.com,所以在StackOverflow中使用這裏是安全的,而不必擔心上傳的圖片會在一段時間後消失。

我提供三個功能:

  • stackImage上傳的表達,導出爲PNG,並返回URL
  • stackMarkdown返回降價,準備複製
  • stackCopyMarkdown副本降價到剪貼板

下一步:創建一個調色板按鈕,自動爲所選圖形自動執行此操作e筆記本。對代碼的改進非常受歡迎。


Needs["JLink`"] 


stackImage::httperr = "Server returned respose code: `1`"; 
stackImage::err = "Server returner error: `1`"; 

stackImage[g_] := 
Module[ 
    {getVal, url, client, method, data, partSource, part, entity, code, 
    response, error, result}, 

    (* this function attempts to parse the response fro the SO server *) 
    getVal[res_, key_String] := 
    With[{k = "var " <> key <> " = "}, 
    StringTrim[ 
    [email protected][[email protected][res, StringMatchQ[#, k ~~ ___] &], 
     k ~~ v___ ~~ ";" :> v], 
    "'"] 
    ]; 

    data = ExportString[g, "PNG"]; 

    JavaBlock[ 
    url = "https://stackoverflow.com/upload/image"; 
    client = JavaNew["org.apache.commons.httpclient.HttpClient"]; 
    method = JavaNew["org.apache.commons.httpclient.methods.PostMethod", url]; 
    partSource = JavaNew["org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource", "mmagraphics.png", MakeJavaObject[data]@toCharArray[]]; 
    part = JavaNew["org.apache.commons.httpclient.methods.multipart.FilePart", "name", partSource]; 
    [email protected]["image/png"]; 
    entity = JavaNew["org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity", {part}, [email protected][]]; 
    [email protected][entity]; 
    code = [email protected][method]; 
    response = [email protected][]; 
    ] 

    If[code =!= 200, Message[stackImage::httperr, code]; Return[$Failed]]; 
    response = StringTrim /@ StringSplit[response, "\n"]; 

    error = getVal[response, "error"]; 
    result = getVal[response, "result"]; 
    If[StringMatchQ[result, "http*"], 
    result, 
    Message[stackImage::err, error]; $Failed] 
    ] 


stackMarkdown[g_] := "![Mathematica graphics](" <> stackImage[g] <> ")" 


stackCopyMarkdown[g_] := Module[{nb, markdown}, 
    markdown = Check[stackMarkdown[g], $Failed]; 
    If[markdown =!= $Failed, 
    nb = NotebookCreate[Visible -> False]; 
    NotebookWrite[nb, Cell[markdown, "Text"]]; 
    SelectionMove[nb, All, Notebook]; 
    FrontEndTokenExecute[nb, "Copy"]; 
    NotebookClose[nb]; 
    ] 
    ] 

更新:

這裏有一個按鈕,將顯示選擇的預覽,並會提供上傳(或取消)。它要求定義以前的功能。

Button["Upload to SO", 
Module[{cell = [email protected][], img}, 
    If[cell =!= {}, img = Rasterize[cell]; 
    MessageDialog[ 
    Column[{"Upload image to StackExchange sites?", 
     img}], {"Upload and copy MarkDown" :> stackCopyMarkdown[img], 
    "Cancel" :> Null}, WindowTitle -> "Upload to StackExchange"]]]] 

可惜我不能把按鈕調色板(CreatePalette),因爲面板的尺寸會影響光柵化。歡迎解決這個問題。

更新2:

基於答案this question,這裏的工作僅限於Windows的面板按鈕:

button = Button["Upload to SO", 
    Module[{sel}, 
    FrontEndExecute[ 
    FrontEndToken[FrontEnd`SelectedNotebook[], "CopySpecial", "MGF"]]; 
    sel = Cases[[email protected]Notebook[], 
    RasterBox[data_, ___] :> 
     Image[data, "Byte", ColorSpace -> "RGB", Magnification -> 1], 
    Infinity]; 
    If[sel =!= {}, 
    With[{img = First[sel]}, 
    MessageDialog[ 
     Column[{"Upload image to StackExchange sites?", 
     img}], {"Upload and copy MarkDown" :> stackCopyMarkdown[img], 
     "Cancel" :> Null}, WindowTitle -> "Upload to StackExchange"] 
    ] 
    ] 
    ] 
    ] 

CreatePalette[button] 

警告:即使您單擊取消在它破壞了剪貼板內容預覽框。

+0

我認爲柵格化調色板問題值得自己去考慮。 –

+0

太棒了!我希望保留我的投票。 *** + 1 *** –

+1

@ Mr.Wizard先檢查一下包裝解決方案的聊天室 – Szabolcs