get.lua 4.2 KB

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