download.go 3.6 KB

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