download.go 3.5 KB

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