2
$\begingroup$

Actually related question 127248. But in that post, the answer from Kuba suggest provide a URL for API-user. But I still hope the API can output the file into the local disk directly. Is it possible?

As the Kuba's comment, the browser cannot write the local disk otherwise it will lead to malicious scripts overflow. But as I know the browser has some ability to write data into disk, such as those video or web-page cache file.

I mean:

FormPage[{"FileNames" -> <|"Interpreter" -> "String", 
    "Hint" -> "File names copy from Excel"|>, 
  "Directory" -> <|"Interpreter" -> "String", 
    "Hint" -> 
     "Specify a directory to download"|>}, (URLDownload[{#FileNames}, \
#Directory]) &, AppearanceRules -> <|"Title" -> "Test API"|>, 
 PageTheme -> "Blue"]

It will download https://i.stack.imgur.com/VULeb.png into my directory E:\document. But if I use CloudDeploy to deploy an API:

CloudDeploy[
 FormPage[{"FileNames" -> <|"Interpreter" -> "String", 
     "Hint" -> "File names copy from Excel"|>, 
   "Directory" -> <|"Interpreter" -> "String", 
     "Hint" -> 
      "Specify a directory to download"|>}, \
(URLDownload[{#FileNames}, #Directory]) &, 
  AppearanceRules -> <|"Title" -> "Test API"|>, 
  PageTheme -> "Blue"], "DownFiles", Permissions -> "Public"]

It cannot into local disk download anymore. How to implement it? If the browser cannot write the data into the E:\document but just can write into some cache directory, I also will accept the way..

$\endgroup$
  • $\begingroup$ As you already mentioned, ordinary browsers won't allow what you want. One final (but highly dangerous) thing to try is building your own browser on top of the Chromium Project. That way, you should be able to customize every aspects of your browser. (Needless to say, you should avoid using it for daily usage.) $\endgroup$ – Silvia Apr 27 '19 at 14:59
  • $\begingroup$ You can't specify where it downloads, but you can download all the files to the default Downloads folder on your friend's machine by returning the appropriate MIMEType for the result. $\endgroup$ – b3m2a1 Apr 29 '19 at 16:33
  • $\begingroup$ Will you have multiple files or just a single file? The single file solution is somewhat easier, but both will be possible. $\endgroup$ – b3m2a1 Apr 29 '19 at 16:45
  • $\begingroup$ @b3m2a1 Multiple files, actually 100+... Could you give further explanation for your solution? $\endgroup$ – yode Apr 29 '19 at 17:37
  • $\begingroup$ @yode I'm thinking basically have your API create a ZIP archive of the files. Then push that to a CloudObject with the right MIMEType and then return the URL to that via HTTPRedirect $\endgroup$ – b3m2a1 May 1 '19 at 19:31
4
+50
$\begingroup$

You can use the channel framework to communicate between your local session and the cloud api you deploy. First create a channel listener with public permissions in your local session:

channel = ChannelListen["test", Permissions -> "Public"]

Next get the hard coded URL to deploy with your api:

In[54]:= channel["URL"]

Out[54]= "https://channelbroker.wolframcloud.com/users/username@wolfram.com/test"

Using this URL, deploy your api function:

CloudDeploy@FormPage[
    {"FileNames"-><|"Interpreter"->"String","Hint"->"File names copy from Excel"|>,"Directory"-><|"Interpreter"->"String","Hint"->"Specify a directory to download"|>}
    ,
    URLRead@
        URLBuild["https://channelbroker.wolframcloud.com/users/username@wolfram.com/test",
            {
                "operation"->"send",
                "Message"->ToString@<|"FileNames"->{#FileNames},"Directory"->#Directory|>
            }]&
    ,
    AppearanceRules-><|"Title"->"Test API"|>,
    PageTheme->"Blue"
]

When receiving the message, use ToExpression to turn the result into an association you can easily work with:

In[53]:= Dynamic@ToExpression@channel["Message"]
Out[53]= <|Message-><|FileNames->{BAR},Directory->BAZ|>|>

Now all that is left to do is make the local session trigger a URLDownload with the parameters sent via your channel. Take a look at the documentation for ChannelListen for many more examples.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Hi, I want build the API for those friend without mathematica to downlaod image bulk. But your solution seem to use Dynamic@ToExpression@channel["Message"] to receive the url? BTW, I get a Missing[NotAvailable]. What I have missed? $\endgroup$ – yode Apr 28 '19 at 6:26
  • $\begingroup$ Ah, I see what you're saying, this is just a good way to make the cloud api communicate with a local Mathematica session.. I am not sure how to accomplish what you want to do, I will think about it more. $\endgroup$ – GenericAccountName Apr 29 '19 at 15:19
  • $\begingroup$ Thank, anyway.. $\endgroup$ – yode Apr 29 '19 at 17:38
  • $\begingroup$ I can't help but think I don't deserve the bounty as I did not answer your question. You didn't even accept my answer.. how does this work? $\endgroup$ – GenericAccountName May 7 '19 at 17:45
  • $\begingroup$ I don't want to waste the bounty. And I feel appreciative for your work still, though I have not accept it. :) $\endgroup$ – yode May 7 '19 at 18:00

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.