You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
3.2 KiB

  1. package main
  2. import (
  3. "code.google.com/p/weed-fs/go/glog"
  4. "code.google.com/p/weed-fs/go/operation"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "net/http"
  9. "os"
  10. "path"
  11. "strings"
  12. )
  13. var (
  14. downloadReplication *string
  15. downloadDir *string
  16. )
  17. func init() {
  18. cmdDownload.Run = runDownload // break init cycle
  19. cmdDownload.IsDebug = cmdDownload.Flag.Bool("debug", false, "verbose debug information")
  20. server = cmdDownload.Flag.String("server", "localhost:9333", "weedfs master location")
  21. downloadDir = cmdDownload.Flag.String("dir", ".", "Download the whole folder recursively if specified.")
  22. }
  23. var cmdDownload = &Command{
  24. UsageLine: "download -server=localhost:9333 -dir=one_directory fid1 [fid2 fid3 ...]",
  25. Short: "download files by file id",
  26. Long: `download files by file id.
  27. Usually you just need to use curl to lookup the file's volume server, and then download them directly.
  28. This download tool combine the two steps into one.
  29. What's more, if you use "weed upload -maxMB=..." option to upload a big file divided into chunks, you can
  30. use this tool to download the chunks and merge them automatically.
  31. `,
  32. }
  33. func runDownload(cmd *Command, args []string) bool {
  34. for _, fid := range args {
  35. filename, content, e := fetchFileId(*server, fid)
  36. if e != nil {
  37. fmt.Println("Fetch Error:", e)
  38. continue
  39. }
  40. if filename == "" {
  41. filename = fid
  42. }
  43. if strings.HasSuffix(filename, "-list") {
  44. filename = filename[0 : len(filename)-len("-list")]
  45. fids := strings.Split(string(content), "\n")
  46. f, err := os.OpenFile(path.Join(*downloadDir, filename), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
  47. if err != nil {
  48. fmt.Println("File Creation Error:", e)
  49. continue
  50. }
  51. defer f.Close()
  52. for _, partId := range fids {
  53. var n int
  54. _, part, err := fetchFileId(*server, partId)
  55. if err == nil {
  56. n, err = f.Write(part)
  57. }
  58. if err == nil && n < len(part) {
  59. err = io.ErrShortWrite
  60. }
  61. if err != nil {
  62. fmt.Println("File Write Error:", err)
  63. break
  64. }
  65. }
  66. } else {
  67. ioutil.WriteFile(path.Join(*downloadDir, filename), content, os.ModePerm)
  68. }
  69. }
  70. return true
  71. }
  72. func fetchFileId(server string, fildId string) (filename string, content []byte, e error) {
  73. fileUrl, lookupError := operation.LookupFileId(server, fildId)
  74. if lookupError != nil {
  75. return "", nil, lookupError
  76. }
  77. filename, content, e = fetchUrl(fileUrl)
  78. return
  79. }
  80. func fetchUrl(fileUrl string) (filename string, content []byte, e error) {
  81. response, err := http.Get(fileUrl)
  82. if err != nil {
  83. return "", nil, err
  84. }
  85. defer response.Body.Close()
  86. contentDisposition := response.Header["Content-Disposition"]
  87. if len(contentDisposition) > 0 {
  88. glog.V(4).Info("Content-Disposition: ", contentDisposition[0])
  89. if strings.HasPrefix(contentDisposition[0], "filename=") {
  90. filename = contentDisposition[0][len("filename="):]
  91. }
  92. } else {
  93. glog.V(4).Info("No Content-Disposition!")
  94. }
  95. content, e = ioutil.ReadAll(response.Body)
  96. return
  97. }
  98. func WriteFile(filename string, data []byte, perm os.FileMode) error {
  99. f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
  100. if err != nil {
  101. return err
  102. }
  103. n, err := f.Write(data)
  104. f.Close()
  105. if err == nil && n < len(data) {
  106. err = io.ErrShortWrite
  107. }
  108. return err
  109. }