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.

138 lines
3.5 KiB

9 years ago
  1. package command
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "os"
  8. "path"
  9. "strings"
  10. "google.golang.org/grpc"
  11. "github.com/seaweedfs/seaweedfs/weed/operation"
  12. "github.com/seaweedfs/seaweedfs/weed/pb"
  13. "github.com/seaweedfs/seaweedfs/weed/security"
  14. "github.com/seaweedfs/seaweedfs/weed/util"
  15. )
  16. var (
  17. d DownloadOptions
  18. )
  19. type DownloadOptions struct {
  20. server *string
  21. dir *string
  22. }
  23. func init() {
  24. cmdDownload.Run = runDownload // break init cycle
  25. d.server = cmdDownload.Flag.String("server", "localhost:9333", "SeaweedFS master location")
  26. d.dir = cmdDownload.Flag.String("dir", ".", "Download the whole folder recursively if specified.")
  27. }
  28. var cmdDownload = &Command{
  29. UsageLine: "download -server=localhost:9333 -dir=one_directory fid1 [fid2 fid3 ...]",
  30. Short: "download files by file id",
  31. Long: `download files by file id.
  32. Usually you just need to use curl to lookup the file's volume server, and then download them directly.
  33. This download tool combine the two steps into one.
  34. What's more, if you use "weed upload -maxMB=..." option to upload a big file divided into chunks, you can
  35. use this tool to download the chunks and merge them automatically.
  36. `,
  37. }
  38. func runDownload(cmd *Command, args []string) bool {
  39. util.LoadConfiguration("security", false)
  40. grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client")
  41. for _, fid := range args {
  42. if e := downloadToFile(func(_ context.Context) pb.ServerAddress { return pb.ServerAddress(*d.server) }, grpcDialOption, fid, util.ResolvePath(*d.dir)); e != nil {
  43. fmt.Println("Download Error: ", fid, e)
  44. }
  45. }
  46. return true
  47. }
  48. func downloadToFile(masterFn operation.GetMasterFn, grpcDialOption grpc.DialOption, fileId, saveDir string) error {
  49. fileUrl, jwt, lookupError := operation.LookupFileId(masterFn, grpcDialOption, fileId)
  50. if lookupError != nil {
  51. return lookupError
  52. }
  53. filename, _, rc, err := util.DownloadFile(fileUrl, jwt)
  54. if err != nil {
  55. return err
  56. }
  57. defer util.CloseResponse(rc)
  58. if filename == "" {
  59. filename = fileId
  60. }
  61. isFileList := false
  62. if strings.HasSuffix(filename, "-list") {
  63. // old command compatible
  64. isFileList = true
  65. filename = filename[0 : len(filename)-len("-list")]
  66. }
  67. f, err := os.OpenFile(path.Join(saveDir, filename), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
  68. if err != nil {
  69. return err
  70. }
  71. defer f.Close()
  72. if isFileList {
  73. content, err := io.ReadAll(rc.Body)
  74. if err != nil {
  75. return err
  76. }
  77. fids := strings.Split(string(content), "\n")
  78. for _, partId := range fids {
  79. var n int
  80. _, part, err := fetchContent(masterFn, grpcDialOption, partId)
  81. if err == nil {
  82. n, err = f.Write(part)
  83. }
  84. if err == nil && n < len(part) {
  85. err = io.ErrShortWrite
  86. }
  87. if err != nil {
  88. return err
  89. }
  90. }
  91. } else {
  92. if _, err = io.Copy(f, rc.Body); err != nil {
  93. return err
  94. }
  95. }
  96. return nil
  97. }
  98. func fetchContent(masterFn operation.GetMasterFn, grpcDialOption grpc.DialOption, fileId string) (filename string, content []byte, e error) {
  99. fileUrl, jwt, lookupError := operation.LookupFileId(masterFn, grpcDialOption, fileId)
  100. if lookupError != nil {
  101. return "", nil, lookupError
  102. }
  103. var rc *http.Response
  104. if filename, _, rc, e = util.DownloadFile(fileUrl, jwt); e != nil {
  105. return "", nil, e
  106. }
  107. defer util.CloseResponse(rc)
  108. content, e = io.ReadAll(rc.Body)
  109. return
  110. }
  111. func WriteFile(filename string, data []byte, perm os.FileMode) error {
  112. f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
  113. if err != nil {
  114. return err
  115. }
  116. n, err := f.Write(data)
  117. f.Close()
  118. if err == nil && n < len(data) {
  119. err = io.ErrShortWrite
  120. }
  121. return err
  122. }