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.

175 lines
4.8 KiB

12 years ago
12 years ago
12 years ago
  1. package main
  2. import (
  3. "code.google.com/p/weed-fs/go/operation"
  4. "code.google.com/p/weed-fs/go/util"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "mime"
  9. "net/url"
  10. "os"
  11. "path"
  12. "path/filepath"
  13. "strconv"
  14. "strings"
  15. )
  16. var (
  17. uploadReplication *string
  18. uploadDir *string
  19. include *string
  20. )
  21. func init() {
  22. cmdUpload.Run = runUpload // break init cycle
  23. cmdUpload.IsDebug = cmdUpload.Flag.Bool("debug", false, "verbose debug information")
  24. server = cmdUpload.Flag.String("server", "localhost:9333", "weedfs master location")
  25. uploadDir = cmdUpload.Flag.String("dir", "", "Upload the whole folder recursively if specified.")
  26. include = cmdUpload.Flag.String("include", "", "pattens of files to upload, e.g., *.pdf, *.html, ab?d.txt, works together with -dir")
  27. uploadReplication = cmdUpload.Flag.String("replication", "", "replication type(000,001,010,100,110,200)")
  28. }
  29. var cmdUpload = &Command{
  30. UsageLine: "upload -server=localhost:9333 file1 [file2 file3]\n upload -server=localhost:9333 -dir=one_directory -include=*.pdf",
  31. Short: "upload one or a list of files",
  32. Long: `upload one or a list of files, or batch upload one whole folder recursively.
  33. If uploading a list of files:
  34. It uses consecutive file keys for the list of files.
  35. e.g. If the file1 uses key k, file2 can be read via k_1
  36. If uploading a whole folder recursively:
  37. All files under the folder and subfolders will be uploaded, each with its own file key.
  38. Optional parameter "-include" allows you to specify the file name patterns.
  39. If any file has a ".gz" extension, the content are considered gzipped already, and will be stored as is.
  40. This can save volume server's gzipped processing and allow customizable gzip compression level.
  41. The file name will strip out ".gz" and stored. For example, "jquery.js.gz" will be stored as "jquery.js".
  42. `,
  43. }
  44. type AssignResult struct {
  45. Fid string `json:"fid"`
  46. Url string `json:"url"`
  47. PublicUrl string `json:"publicUrl"`
  48. Count int
  49. Error string `json:"error"`
  50. }
  51. func Assign(server string, count int) (*AssignResult, error) {
  52. values := make(url.Values)
  53. values.Add("count", strconv.Itoa(count))
  54. if *uploadReplication != "" {
  55. values.Add("replication", *uploadReplication)
  56. }
  57. jsonBlob, err := util.Post("http://"+server+"/dir/assign", values)
  58. debug("assign result :", string(jsonBlob))
  59. if err != nil {
  60. return nil, err
  61. }
  62. var ret AssignResult
  63. err = json.Unmarshal(jsonBlob, &ret)
  64. if err != nil {
  65. return nil, err
  66. }
  67. if ret.Count <= 0 {
  68. return nil, errors.New(ret.Error)
  69. }
  70. return &ret, nil
  71. }
  72. func upload(filename string, server string, fid string) (int, error) {
  73. debug("Start uploading file:", filename)
  74. fh, err := os.Open(filename)
  75. if err != nil {
  76. debug("Failed to open file:", filename)
  77. return 0, err
  78. }
  79. fi, fiErr := fh.Stat()
  80. if fiErr != nil {
  81. debug("Failed to stat file:", filename)
  82. return 0, fiErr
  83. }
  84. filename = path.Base(filename)
  85. isGzipped := path.Ext(filename) == ".gz"
  86. if isGzipped {
  87. filename = filename[0 : len(filename)-3]
  88. }
  89. mtype := mime.TypeByExtension(strings.ToLower(filepath.Ext(filename)))
  90. ret, e := operation.Upload("http://"+server+"/"+fid+"?ts="+strconv.Itoa(int(fi.ModTime().Unix())), filename, fh, isGzipped, mtype)
  91. if e != nil {
  92. return 0, e
  93. }
  94. return ret.Size, e
  95. }
  96. type SubmitResult struct {
  97. FileName string `json:"fileName"`
  98. FileUrl string `json:"fileUrl"`
  99. Fid string `json:"fid"`
  100. Size int `json:"size"`
  101. Error string `json:"error"`
  102. }
  103. func submit(files []string) ([]SubmitResult, error) {
  104. results := make([]SubmitResult, len(files))
  105. for index, file := range files {
  106. results[index].FileName = file
  107. }
  108. ret, err := Assign(*server, len(files))
  109. if err != nil {
  110. for index, _ := range files {
  111. results[index].Error = err.Error()
  112. }
  113. return results, err
  114. }
  115. for index, file := range files {
  116. fid := ret.Fid
  117. if index > 0 {
  118. fid = fid + "_" + strconv.Itoa(index)
  119. }
  120. results[index].Size, err = upload(file, ret.PublicUrl, fid)
  121. if err != nil {
  122. fid = ""
  123. results[index].Error = err.Error()
  124. }
  125. results[index].Fid = fid
  126. results[index].FileUrl = ret.PublicUrl + "/" + fid
  127. }
  128. return results, nil
  129. }
  130. func runUpload(cmd *Command, args []string) bool {
  131. if len(cmdUpload.Flag.Args()) == 0 {
  132. if *uploadDir == "" {
  133. return false
  134. }
  135. filepath.Walk(*uploadDir, func(path string, info os.FileInfo, err error) error {
  136. if err == nil {
  137. if !info.IsDir() {
  138. if *include != "" {
  139. if ok, _ := filepath.Match(*include, filepath.Base(path)); !ok {
  140. return nil
  141. }
  142. }
  143. results, e := submit([]string{path})
  144. bytes, _ := json.Marshal(results)
  145. fmt.Println(string(bytes))
  146. if e != nil {
  147. return e
  148. }
  149. }
  150. } else {
  151. fmt.Println(err)
  152. }
  153. return err
  154. })
  155. } else {
  156. results, _ := submit(args)
  157. bytes, _ := json.Marshal(results)
  158. fmt.Println(string(bytes))
  159. }
  160. return true
  161. }