(* Mathematica Package *) (* :Title: CopyRemote *) (* :Context: CopyRemote` *) (* :Author: Rolf Mertig GluonVision GmbH http://www.mertig.com *) (* :Package Version: 2.0 *) (* :Mathematica Versions: 7 - 9 *) (* :License: LGPL *) (* :Copyright: Rolf Mertig, 2002 - 2013. *) (* :Installation: This package can be invoked without local installation by Import["http://www.mertig.com/mathdepot/CopyRemote.m"]; *) (* :Discussion: CopyRemote copies a file from a url to a local directory. The code is based on the GetRemote example from the JLink documentation Furthermore OpenRemote, URLQ and URLFileByteCount are implemented. *) (* "Example usage: CopyRemote["http://functions.wolfram.com/NB/Hypergeometric2F1.nb"] copies the notebook to $TemporaryDirectory To also open the notebook, do: OpenRemote["http://functions.wolfram.com/NB/Hypergeometric2F1.nb"] This copies a palette to the right place: NotebookOpen @ CopyRemote["https://dl.dropbox.com/u/38623/SE%20Uploader.nb", FileNameJoin[{$UserAddOnsDirectory,"SystemFiles","FrontEnd","Palettes"}]] Notice: Since with default Options[CopyRemote] "%20" will be replaced by " ", so this: will put "SE Uploader.nb" into the Palettes directory An alternative is to use Import["http://www.mertig.com/mathdepot/Installer.m"]; InstallPalette["https://dl.dropbox.com/u/38623/SE%20Uploader.nb"] *) (* :Keywords: projects, install *) BeginPackage["CopyRemote`",{"JLink`"}]; (* enable updating without reloading. URLQ is memoizing, so leave it unprotected *) Unprotect @@ { CopyRemote, OpenRemote, URLFileByteCount}; ClearAll @@ { CopyRemote, OpenRemote, URLFileByteCount}; ClearAll[URLQ]; CopyRemote::usage = "CopyRemote[urlfile] copies a urlfile as URLFileNameTake[urlfile] to $TemporaryDirectory. CopyRemote[url, localfile] copies a file from an http location to localfile."; OpenRemote::usage = "OpenRemote[urfile] is a utility function for basically SystemOpen[CopyRemote[urlfile]]." ProxyHost::usage = "ProxyHost is an option for CopyRemote."; ProxyPort::usage = "ProxyPort is an option for CopyRemote."; URLFileByteCount::usage = "URLFileByteCount[file] gives the remote file size in Byte." URLFileNameTake::usage = "URLFileNameTake[file] is like FileNameTake but skpips everything after a ?"; URLQ::usage = "URLQ[url] give True if url is reachable and False otherwise."; CopyRemote::failed = "The transfer of `1` did not succeed. Please try again."; Begin["`Private`"]; (* the option StringReplace is to unescape URL-file artifacts like %20 *) (* if Print is set to True then a Monitor shows up displaying the percentage of tranfer *) Options[CopyRemote] = {ProxyHost :> None, ProxyPort :> None, Print -> True, StringReplace -> {"%20"->" "}}; Options[OpenRemote] = Options[CopyRemote]; (* CopyRemote might return $Failed, therefore, only open the result if it was successful : *) OpenRemote[args__] := Module[ {cr}, Replace[cr = CopyRemote[args], (s_String?FileExistsQ) :> SystemOpen[s]]; cr ]; URLFileNameTake[s_String] := StringSplit[FileNameTake[s],"&"]//First; filename = Function[{f,s}, StringReplace[ URLFileNameTake[f], s]]; (* use $TemporaryDirectory if no second argument is given *) CopyRemote[url_?URLQ, opts:OptionsPattern[]] := CopyRemote[url, $TemporaryDirectory, FileNameJoin[{$TemporaryDirectory, filename[url, OptionValue[StringReplace]]}], opts]; (* create directory if it does not exist *) CopyRemote[url_?URLQ, file_String /;( (DirectoryName[file]=!="") && (FileType[DirectoryName[file]] =!= Directory) ), opts___?OptionQ_ ] := Module[ {cdir}, Catch[ cdir = CreateDirectory[DirectoryName @ file]; If[ cdir === $Failed, Throw[$Failed] ]; CopyRemote[url, cdir, FileNameTake[file], opts] ] ]; (* since it will put files in Directory[] otherwise, redefine natural calls like CopyRemote["http://www.mertig.com/mathdepot/CopyRemote.m", "CopyRemote.m"] to mean CopyRemote["http://www.mertig.com/mathdepot/CopyRemote.m", FileNameJoin[{$TemporaryDirectory, "CopyRemote.m"}]] *) CopyRemote[url_?URLQ, file_String /; (FileType[file] === None), more___ ] /; (file === FileNameTake[file]) := CopyRemote[url, $TemporaryDirectory, file]; (* go from 2 argumetn to three argument form: *) CopyRemote[url_?URLQ, file_String /;( (DirectoryName[file]=!="") && (FileType[DirectoryName[file]] === Directory) ), opts___?OptionQ_ ] := CopyRemote[url, DirectoryName[file], file, opts]; (* if the directory exists, use it and get the filename from the url filename *) CopyRemote[url_?URLQ, dir_String /; FileType[dir] === Directory, opts:OptionsPattern[] ] := CopyRemote[url, dir, filename[url, OptionValue[StringReplace] ], opts]; (* a NotebookClose function which does nothing if $Notebooks is False *) closenb = Function[locnb, If[ $Notebooks && StringMatchQ[locnb, "*.nb", IgnoreCase -> True], Select[Notebooks[], ToFileName[ "FileName" /. NotebookInformation[#]] === locnb &] /. {n_NotebookObject} :> NotebookClose[n] ]]; CopyRemote[url_?URLQ, localdir_String?DirectoryQ, locfile_String, opts:OptionsPattern[] ] := Catch @ Block[ {openStream, read, close, locfilefull, locfiletmp, outFile, rfilesize}, Needs["JLink`"]; Symbol["JLink`InstallJava"][]; (* using Symbol here enables an .mx saveable package, or to put this into a ButtonFunction, etc. *) locfilefull = If[ DirectoryName[locfile]==="", FileNameJoin[{localdir, locfile}], locfile ]; (* download to a temporary file , in case the download fails. copy locfiletmp to locfilefull only if download succeeded *) (* This code is based on the GetRemote example in the JLink documentation *) rfilesize = URLFileByteCount[url]; If[ !IntegerQ[rfilesize], Message[CopyRemote::failed, url]; Throw[$Failed] ]; (* temporary file *) outFile = OpenWrite[DOSTextFormat -> False]; locfiletmp = Function[j, If[ OptionValue[Print], Monitor[j, progress[url, outFile[[1]], rfilesize]], j ], HoldFirst ][ Symbol["JLink`JavaBlock"][ Module[ {u, stream, numRead, buf, prxyHost, prxyPort}, {prxyHost, prxyPort} = OptionValue/@{ProxyHost, ProxyPort}; If[ StringQ[prxyHost], (* Set properties to force use of proxy. *) Symbol["JLink`SetInternetProxy"][prxyHost, prxyPort] ]; u = Symbol["JLink`JavaNew"]["java.net.URL", url]; stream = u@openStream[]; If[ stream === $Failed, Return[$Failed] ]; buf = Symbol["JLink`JavaNew"]["[B", 8192]; (* ] *) While[(numRead = stream@read[buf]) > 0, WriteString[outFile, FromCharacterCode[If[ # < 0, #+256, # ]& /@ Take[Symbol["JLink`Val"][buf], numRead]]] ]; stream@close[]; Close[outFile] (* Close returns the filename *) ] ] ]; (*check if the transfer was successfull: *) (* TODO: add MD5sum check here somehow *) (* sometimes rfilesize is -1 ... : *) If[ rfilesize > 0, If[ FileByteCount[locfiletmp] =!= rfilesize, Message[CopyRemote::failed, url]; Throw[$Failed] ] ]; (* locfilefull can be a notebook. If it is open, close it *) closenb @ locfilefull; If[ FileExistsQ[locfilefull], DeleteFile[locfilefull] ]; RenameFile[locfiletmp, locfilefull] ]; (* does the URL exists or not *) URLQ[link_String] := URLQ[link] = (* memoize links, to save time *) Catch @ Block[ {openConnection, getContentLength, getInputstream, check, close, getInputStream}, If[ (!StringMatchQ[StringTrim@link, "http://*"] ) && (!StringMatchQ[StringTrim@link, "https://*"] ), Throw[False] ]; checknetwork[]; (* maybe not necessary, but well ... *) Needs["JLink`"]; Symbol["JLink`InstallJava"][]; Symbol["JLink`JavaBlock"][ Module[ {url, urlcon}, url = Symbol["JavaNew"]["java.net.URL", StringTrim@link]; urlcon = url@openConnection[]; Quiet[check = urlcon@getInputStream[]]; If[ check === $Failed, False, check@close[]; True ] ]] ]; URLQ[h_/;Head[h] =!= String] = False; (* find out how big the remote file is *) URLFileByteCount[link_?URLQ] := Block[ {openConnection, getContentLength, getInputstream, check, close, getInputStream}, Needs["JLink`"]; Symbol["JLink`InstallJava"][]; Symbol["JLink`JavaBlock"][ Module[ {url, urlcon, len}, url = Symbol["JavaNew"]["java.net.URL", link]; urlcon = url@openConnection[]; Quiet[check = urlcon@getInputStream[]]; If[ check === $Failed, $Failed, len = urlcon@getContentLength[]; check@close[]; len ] ]] ]; $progressupdateinterval = .6; myroundMB[rfs_?NumberQ] := Round[100 rfs /1024.^2]/ 100.; myroundMB[_] := " "; Clear[progress]; progress[remotefile_?URLQ, localfile_String, rfilesize_Integer] := If[ $Notebooks, Row[{"Copied ", ProgressIndicator[ Quiet[If[ ! NumberQ[#], 0, # ] &@(Refresh[FileByteCount[localfile], TrackedSymbols -> {}, UpdateInterval -> $progressupdateinterval]/rfilesize)], Background-> Orange, ImageSize->{42, 15} ], " ", If[ ! NumberQ[Setting@#], 0, # ] &@ Refresh[Round[100 FileByteCount[localfile]/rfilesize], TrackedSymbols -> {}, UpdateInterval -> $progressupdateinterval ], " % of ", myroundMB[rfilesize], " MB ", "from ", remotefile, " to ", localfile }], Print["Transferring ", remotefile," please wait "] ]; (* check for a setting in the Mathematica preferences *) checknetwork[] := If[ ("AllowInternetUse" /. SystemInformation["Network"]) === False, Print["You have configured Mathematica to not access the internet. Too bad. Please check the \"Allow Mathematica to use the Internet\" box in the Help \[FilledRightTriangle] Internet Connectivity dialog. " ]; Throw[$Failed] ]; With[ {list = { CopyRemote, OpenRemote, URLFileByteCount}}, SetAttributes[list, ReadProtected]; Protect @ list; ]; End[]; EndPackage[]; (* Test[ URLQ["http://mathematica.stackexchange.com"], True ] Test[ URLQ["http://www.mertig.cddm"] , False ] Test[URLFileByteCount[ "http://www.nist.gov/images/banner_graphics/homepage_banner.jpg"], 44850] CopyRemote["http://www.mertig.com/mathdepot/CopyRemote.m"] *)