12
$\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$
3
  • 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, 2014 at 10:44
  • $\begingroup$ Tried, failed. At least for MediaWiki, all parameters have to come as parts of of the multipart. $\endgroup$ Jul 9, 2014 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, 2015 at 17:39

1 Answer 1

13
$\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$
5
  • 2
    $\begingroup$ Thanks for the help answering another question! $\endgroup$ Oct 24, 2015 at 21:52
  • $\begingroup$ Does the MultipartElements option still accept a third byte list argument? $\endgroup$ Oct 24, 2015 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, 2015 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$ Oct 27, 2015 at 1:54
  • $\begingroup$ Wow,have you notice this post?Maybe the File can solve? $\endgroup$
    – yode
    Oct 14, 2016 at 16:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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