8
$\begingroup$

I'm trying to upload a file to a wiki using Mathematica's URLFetch. Some preliminary work is required - one needs to log in to the wiki, obtain an edit token, and exchange some cookies with the wiki server. URLFetch can handle all of that. But then for the actual file upload, the MediaWiki API (e.g. http://www.mediawiki.org/wiki/API:Upload) specifies that one has to use the POST method with Content-Type=multipart/form-data, but the Mathematica implementation of multipart within URLFetch seems to be incomplete.

The Mathematica documentation of the option "MultipartData" of URLFetch says only:

to upload multipart data, each part must be of the form {name, mimeType, {bytes}}, where {bytes} is a list of bytes

This suggests that in the "Content-Disposition" header of each part of a multipart request, a name can be specified as in:

Content-Disposition: form-data; name="file"

but there seems to be no way to also specify a filename, as in

Content-Disposition: form-data; name="file"; filename="Apple.gif"

Finally, the MediaWiki API requires that a file name be specified; see e.g. the example of a multipart request at the very bottom of http://www.mediawiki.org/wiki/API:Upload (and when I try to upload a file without specifying a filename, the wiki server sends back an error message).

So my question: Is there a more complete implementation of the MultiPart protocol with URLFetch?

$\endgroup$
  • 1
    $\begingroup$ Try to use MultiPartData for the file and then the Parameters option (see the section on options) for the file name. $\endgroup$ – C. E. Jul 9 '14 at 10:44
  • $\begingroup$ Tried, failed. At least for MediaWiki, all parameters have to come as parts of of the multipart. $\endgroup$ – Dror Bar-Natan Jul 9 '14 at 11:11
  • $\begingroup$ Dror Bar Natan, I have the same problem with URLFetch MultipartData / MultipartElements you had almost a year ago. Did you find a solution? $\endgroup$ – pvanbijnen Apr 11 '15 at 17:39
9
$\begingroup$

This is a hack, but it works.

UploadFile[url_, filePath_, urlParams___] := With[
  {
    bytes = Import[filePath, "Byte"],
    filename = StringJoin[FileBaseName[filePath], ".", FileExtension[filePath]]
  },
  URLExecute[
    url,
    urlParams,
    "Method" -> "POST",
    "MultipartElements" -> {
      {"file\"; filename=\"" <> filename, "application/octet-stream", bytes}
    },
    "Headers" -> {
      "Accept" -> "application/json; charset=UTF-8",
      "Content-Type" -> "multipart/form-data",
      "Expect" -> "" (* See edit 2.5 below *)
    }
  ]
]

Notice that I'm stuffing the filename parameter in the first element of "MultipartElements" along with the field name in order to get it in Content-Disposition for that element.

(* Get a new ephemeral RequestBin from http://requestb.in/, then put URL below: *)
UploadFile["http://requestb.in/1hyjbdl1", "ExampleData/rose.gif"]

result:

Content-Disposition: form-data; name="file"; filename="rose.gif"

Theoretically you could also specify "Content-Disposition" -> "form-data; filename=\"foo\"" in the request header, but the server would have to be expecting exactly one file in the form-data and parse accordingly, whereas the above approach with filenames in each part I think is more commonly expected.


Edit

In version 11, there's a no-hack way to do this:

image = FindFile["ExampleData/Ocelot.jpg"];
req = HTTPRequest[url, <|
  "Body" -> {"image" -> File[image]},
  "Expect" -> "" (* See edit 2.5 below *)
|>];
URLRead[req, "Body"]

Docs (see Scope > "To send files...")

Edit 2, 2.5 Mathematica's HTTP client uses Expect: "100-continue", which is not supported by some common load balancers that may be between you and your server. To disable that, add the header "Expect" -> "".

$\endgroup$
  • 2
    $\begingroup$ Thanks for the help answering another question! $\endgroup$ – Christopher Haydock Oct 24 '15 at 21:52
  • $\begingroup$ Does the MultipartElements option still accept a third byte list argument? $\endgroup$ – Christopher Haydock Oct 24 '15 at 21:52
  • $\begingroup$ @ChristopherHaydock yes, that's the bytes symbol in my example, if I understand what you're expecting. Thanks for the reference from the other answer :) $\endgroup$ – ZachB Oct 25 '15 at 18:36
  • $\begingroup$ Oops! I was confused about this point because I ran a "MultipartElements" syntax test that specified elements in mixed format: {"name","mimetype"} -> string and {"name","mimetype",byte-list}. This does not work. The same syntax must be used for all elements. $\endgroup$ – Christopher Haydock Oct 27 '15 at 1:54
  • $\begingroup$ Wow,have you notice this post?Maybe the File can solve? $\endgroup$ – yode Oct 14 '16 at 16:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.