53

这是对所有mathematica标签追随者的挑战。让我们通过创建一个imgur上传器将图像插入到 Mathematica 的 SO 帖子中变得更加方便。

我们如何创建一个函数imgur[g_]来栅格化其参数(确保最终大小不大于 StackOverflow 帖子的宽度),将其转换为 PNG,将其上传到 imgur,并返回准备好粘贴的MarkDown行,例如![Mathematica graphic](http://i.imgur.com/ZENa4.jpg)?

有用的参考资料:

我未能将后一种方法调整为上传图像而不先将其导出到文件中。


警告,小心使用!StackOverflow 使用单独的 imgur 安装来无限期地保留图像。如果您使用主 imgur,如果没有人查看,图像将在 6 个月后消失。不幸的是,截至 2011 年 11 月,似乎没有官方方法可以以编程方式将图像上传到 StackOverflow。


更新: 见下面直接上传到 StackOverflow 的解决方案。

4

3 回答 3

16

一只小鸟刚刚告诉我这个问题的Mathematica解决方案(底层实现仍然使用 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"没有记录和实验性(因此可能会发生变化)。

于 2011-12-01T17:32:59.110 回答
13

注意:在此处获取具有此功能的现成调色板。


Arnoud 的解决方案让我既兴奋又不耐烦,所以这里对其进行了改进。如果不研究他的代码,我不可能做到这一点。这个版本似乎更可靠,更不容易出现超时错误,但说实话,我根本不懂 Java,所以欢迎任何改进。

最重要的是:这个版本是stack.imgur.com直接上传到的,所以在 StackOverflow 上使用是安全的,不用担心上传的图片会在一段时间后消失。

我提供三个功能:

  • stackImage上传表达式,导出为 PNG,并返回 URL
  • stackMarkdown返回降价,准备复制
  • stackCopyMarkdown将降价复制到剪贴板

下一步:创建一个调色板按钮,该按钮会自动为笔记本中的选定图形执行此操作。非常欢迎对代码进行改进。


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[
     First@StringCases[First@Select[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];
    part@setContentType["image/png"];
    entity = JavaNew["org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity", {part}, method@getParams[]];
    method@setRequestEntity[entity];
    code = client@executeMethod[method];
    response = method@getResponseBodyAsString[];
  ]

  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 = NotebookRead@InputNotebook[], 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:

根据对这个问题的回答,这里有一个仅适用于 Windows 的调色板按钮:

button = Button["Upload to SO",
  Module[{sel},
   FrontEndExecute[
    FrontEndToken[FrontEnd`SelectedNotebook[], "CopySpecial", "MGF"]];
   sel = Cases[NotebookGet@ClipboardNotebook[], 
     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]

警告:即使您在预览框中单击取消,它也会破坏剪贴板内容。

于 2011-12-01T15:04:18.750 回答
12

注意:这是使用匿名 imgur 上传器和我的匿名密钥。imgur 站点将上传限制为每小时 50 次上传,这通常应该没问题,但是如果很多人同时尝试,这可能会导致问题。所以请在这里获取您自己的匿名密钥:

http://imgur.com/register/api_anon

然后用您自己的密钥替换下面代码中的密钥(谢谢!)。

代码中最棘手的部分是从 Mathematica 表达式到 PNG 图像到 Base64 编码到 URL 编码的转换。大约有 1000 种方法做错了,我想我设法全部尝试了。

代码分为几部分:

  • 构造 POST url
  • 建立 HTTP 连接
  • 发送 POST 网址
  • 读回结果,即 XML
  • 从 XML 中提取 imgur url
  • 将 imgur url 格式化为 markdown(或Mathematica Hyperlink函数)。

这是代码:

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 = jUrl@openConnection[];
   jConn@setDoOutput[True];
   jWriter =
    JLink`JavaNew["java.io.OutputStreamWriter",
     jConn@getOutputStream[]];
   jWriter@write[data];
   jWriter@flush[];
   jInput = jConn@getInputStream[];
   buffer = {};
   While[(byte = jInput@read[]; 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[Binarize@g, 10], g}];
       imgur[pic]

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

和实际图像:

数学图形

于 2011-12-01T03:29:54.867 回答