1----------------------------------------------------------------------------- 2-- Little program to download files from URLs 3-- LuaSocket sample files 4-- Author: Diego Nehab 5----------------------------------------------------------------------------- 6local socket = require("socket") 7local http = require("socket.http") 8local ftp = require("socket.ftp") 9local url = require("socket.url") 10local ltn12 = require("ltn12") 11 12-- formats a number of seconds into human readable form 13function nicetime(s) 14 local l = "s" 15 if s > 60 then 16 s = s / 60 17 l = "m" 18 if s > 60 then 19 s = s / 60 20 l = "h" 21 if s > 24 then 22 s = s / 24 23 l = "d" -- hmmm 24 end 25 end 26 end 27 if l == "s" then return string.format("%5.0f%s", s, l) 28 else return string.format("%5.2f%s", s, l) end 29end 30 31-- formats a number of bytes into human readable form 32function nicesize(b) 33 local l = "B" 34 if b > 1024 then 35 b = b / 1024 36 l = "KB" 37 if b > 1024 then 38 b = b / 1024 39 l = "MB" 40 if b > 1024 then 41 b = b / 1024 42 l = "GB" -- hmmm 43 end 44 end 45 end 46 return string.format("%7.2f%2s", b, l) 47end 48 49-- returns a string with the current state of the download 50local remaining_s = "%s received, %s/s throughput, %2.0f%% done, %s remaining" 51local elapsed_s = "%s received, %s/s throughput, %s elapsed " 52function gauge(got, delta, size) 53 local rate = got / delta 54 if size and size >= 1 then 55 return string.format(remaining_s, nicesize(got), nicesize(rate), 56 100*got/size, nicetime((size-got)/rate)) 57 else 58 return string.format(elapsed_s, nicesize(got), 59 nicesize(rate), nicetime(delta)) 60 end 61end 62 63-- creates a new instance of a receive_cb that saves to disk 64-- kind of copied from luasocket's manual callback examples 65function stats(size) 66 local start = socket.gettime() 67 local last = start 68 local got = 0 69 return function(chunk) 70 -- elapsed time since start 71 local current = socket.gettime() 72 if chunk then 73 -- total bytes received 74 got = got + string.len(chunk) 75 -- not enough time for estimate 76 if current - last > 1 then 77 io.stderr:write("\r", gauge(got, current - start, size)) 78 io.stderr:flush() 79 last = current 80 end 81 else 82 -- close up 83 io.stderr:write("\r", gauge(got, current - start), "\n") 84 end 85 return chunk 86 end 87end 88 89-- determines the size of a http file 90function gethttpsize(u) 91 local r, c, h = http.request {method = "HEAD", url = u} 92 if c == 200 then 93 return tonumber(h["content-length"]) 94 end 95end 96 97-- downloads a file using the http protocol 98function getbyhttp(u, file) 99 local save = ltn12.sink.file(file or io.stdout) 100 -- only print feedback if output is not stdout 101 if file then save = ltn12.sink.chain(stats(gethttpsize(u)), save) end 102 local r, c, h, s = http.request {url = u, sink = save } 103 if c ~= 200 then io.stderr:write(s or c, "\n") end 104end 105 106-- downloads a file using the ftp protocol 107function getbyftp(u, file) 108 local save = ltn12.sink.file(file or io.stdout) 109 -- only print feedback if output is not stdout 110 -- and we don't know how big the file is 111 if file then save = ltn12.sink.chain(stats(), save) end 112 local gett = url.parse(u) 113 gett.sink = save 114 gett.type = "i" 115 local ret, err = ftp.get(gett) 116 if err then print(err) end 117end 118 119-- determines the scheme 120function getscheme(u) 121 -- this is an heuristic to solve a common invalid url poblem 122 if not string.find(u, "//") then u = "//" .. u end 123 local parsed = url.parse(u, {scheme = "http"}) 124 return parsed.scheme 125end 126 127-- gets a file either by http or ftp, saving as <name> 128function get(u, name) 129 local fout = name and io.open(name, "wb") 130 local scheme = getscheme(u) 131 if scheme == "ftp" then getbyftp(u, fout) 132 elseif scheme == "http" then getbyhttp(u, fout) 133 else print("unknown scheme" .. scheme) end 134end 135 136-- main program 137arg = arg or {} 138if table.getn(arg) < 1 then 139 io.write("Usage:\n lua get.lua <remote-url> [<local-file>]\n") 140 os.exit(1) 141else get(arg[1], arg[2]) end 142