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.

226 lines
5.7 KiB

12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
  1. package storage
  2. import (
  3. "encoding/hex"
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. "mime"
  8. "net/http"
  9. "path"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/chrislusf/seaweedfs/go/glog"
  14. "github.com/chrislusf/seaweedfs/go/images"
  15. "github.com/chrislusf/seaweedfs/go/util"
  16. "github.com/chrislusf/seaweedfs/go/operation"
  17. )
  18. const (
  19. NeedleHeaderSize = 16 //should never change this
  20. NeedlePaddingSize = 8
  21. NeedleChecksumSize = 4
  22. MaxPossibleVolumeSize = 4 * 1024 * 1024 * 1024 * 8
  23. )
  24. /*
  25. * A Needle means a uploaded and stored file.
  26. * Needle file size is limited to 4GB for now.
  27. */
  28. type Needle struct {
  29. Cookie uint32 `comment:"random number to mitigate brute force lookups"`
  30. Id uint64 `comment:"needle id"`
  31. Size uint32 `comment:"sum of DataSize,Data,NameSize,Name,MimeSize,Mime"`
  32. DataSize uint32 `comment:"Data size"` //version2
  33. Data []byte `comment:"The actual file data"`
  34. Flags byte `comment:"boolean flags"` //version2
  35. NameSize uint8 //version2
  36. Name []byte `comment:"maximum 256 characters"` //version2
  37. MimeSize uint8 //version2
  38. Mime []byte `comment:"maximum 256 characters"` //version2
  39. LastModified uint64 //only store LastModifiedBytesLength bytes, which is 5 bytes to disk
  40. Ttl *TTL
  41. Checksum CRC `comment:"CRC32 to check integrity"`
  42. Padding []byte `comment:"Aligned to 8 bytes"`
  43. }
  44. func (n *Needle) String() (str string) {
  45. str = fmt.Sprintf("Cookie:%d, Id:%d, Size:%d, DataSize:%d, Name: %s, Mime: %s", n.Cookie, n.Id, n.Size, n.DataSize, n.Name, n.Mime)
  46. return
  47. }
  48. func ParseUpload(r *http.Request) (fileName string, data []byte, mimeType string, isGzipped bool, modifiedTime uint64, ttl *TTL, isChunkedFile bool, e error) {
  49. form, fe := r.MultipartReader()
  50. if fe != nil {
  51. glog.V(0).Infoln("MultipartReader [ERROR]", fe)
  52. e = fe
  53. return
  54. }
  55. //first multi-part item
  56. part, fe := form.NextPart()
  57. if fe != nil {
  58. glog.V(0).Infoln("Reading Multi part [ERROR]", fe)
  59. e = fe
  60. return
  61. }
  62. fileName = part.FileName()
  63. if fileName != "" {
  64. fileName = path.Base(fileName)
  65. }
  66. data, e = ioutil.ReadAll(part)
  67. if e != nil {
  68. glog.V(0).Infoln("Reading Content [ERROR]", e)
  69. return
  70. }
  71. //if the filename is empty string, do a search on the other multi-part items
  72. for fileName == "" {
  73. part2, fe := form.NextPart()
  74. if fe != nil {
  75. break // no more or on error, just safely break
  76. }
  77. fName := part2.FileName()
  78. //found the first <file type> multi-part has filename
  79. if fName != "" {
  80. data2, fe2 := ioutil.ReadAll(part2)
  81. if fe2 != nil {
  82. glog.V(0).Infoln("Reading Content [ERROR]", fe2)
  83. e = fe2
  84. return
  85. }
  86. //update
  87. data = data2
  88. fileName = path.Base(fName)
  89. break
  90. }
  91. }
  92. dotIndex := strings.LastIndex(fileName, ".")
  93. ext, mtype := "", ""
  94. if dotIndex > 0 {
  95. ext = strings.ToLower(fileName[dotIndex:])
  96. mtype = mime.TypeByExtension(ext)
  97. }
  98. contentType := part.Header.Get("Content-Type")
  99. if contentType != "" && mtype != contentType {
  100. mimeType = contentType //only return mime type if not deductable
  101. mtype = contentType
  102. }
  103. if part.Header.Get("Content-Encoding") == "gzip" {
  104. isGzipped = true
  105. } else if operation.IsGzippable(ext, mtype) {
  106. if data, e = operation.GzipData(data); e != nil {
  107. return
  108. }
  109. isGzipped = true
  110. }
  111. if ext == ".gz" {
  112. isGzipped = true
  113. }
  114. if strings.HasSuffix(fileName, ".gz") &&
  115. !strings.HasSuffix(fileName, ".tar.gz") {
  116. fileName = fileName[:len(fileName)-3]
  117. }
  118. modifiedTime, _ = strconv.ParseUint(r.FormValue("ts"), 10, 64)
  119. ttl, _ = ReadTTL(r.FormValue("ttl"))
  120. isChunkedFile, _ = strconv.ParseBool(r.FormValue("cm"))
  121. return
  122. }
  123. func NewNeedle(r *http.Request, fixJpgOrientation bool) (n *Needle, e error) {
  124. fname, mimeType, isGzipped, isChunkedFile := "", "", false, false
  125. n = new(Needle)
  126. fname, n.Data, mimeType, isGzipped, n.LastModified, n.Ttl, isChunkedFile, e = ParseUpload(r)
  127. if e != nil {
  128. return
  129. }
  130. if len(fname) < 256 {
  131. n.Name = []byte(fname)
  132. n.SetHasName()
  133. }
  134. if len(mimeType) < 256 {
  135. n.Mime = []byte(mimeType)
  136. n.SetHasMime()
  137. }
  138. if isGzipped {
  139. n.SetGzipped()
  140. }
  141. if n.LastModified == 0 {
  142. n.LastModified = uint64(time.Now().Unix())
  143. }
  144. n.SetHasLastModifiedDate()
  145. if n.Ttl != EMPTY_TTL {
  146. n.SetHasTtl()
  147. }
  148. if isChunkedFile {
  149. n.SetChunkManifest()
  150. }
  151. if fixJpgOrientation {
  152. loweredName := strings.ToLower(fname)
  153. if mimeType == "image/jpeg" || strings.HasSuffix(loweredName, ".jpg") || strings.HasSuffix(loweredName, ".jpeg") {
  154. n.Data = images.FixJpgOrientation(n.Data)
  155. }
  156. }
  157. n.Checksum = NewCRC(n.Data)
  158. commaSep := strings.LastIndex(r.URL.Path, ",")
  159. dotSep := strings.LastIndex(r.URL.Path, ".")
  160. fid := r.URL.Path[commaSep+1:]
  161. if dotSep > 0 {
  162. fid = r.URL.Path[commaSep+1 : dotSep]
  163. }
  164. e = n.ParsePath(fid)
  165. return
  166. }
  167. func (n *Needle) ParsePath(fid string) (err error) {
  168. length := len(fid)
  169. if length <= 8 {
  170. return errors.New("Invalid fid:" + fid)
  171. }
  172. delta := ""
  173. deltaIndex := strings.LastIndex(fid, "_")
  174. if deltaIndex > 0 {
  175. fid, delta = fid[0:deltaIndex], fid[deltaIndex+1:]
  176. }
  177. n.Id, n.Cookie, err = ParseKeyHash(fid)
  178. if err != nil {
  179. return err
  180. }
  181. if delta != "" {
  182. if d, e := strconv.ParseUint(delta, 10, 64); e == nil {
  183. n.Id += d
  184. } else {
  185. return e
  186. }
  187. }
  188. return err
  189. }
  190. func ParseKeyHash(key_hash_string string) (uint64, uint32, error) {
  191. if len(key_hash_string)%2 == 1 {
  192. key_hash_string = "0" + key_hash_string
  193. }
  194. key_hash_bytes, khe := hex.DecodeString(key_hash_string)
  195. key_hash_len := len(key_hash_bytes)
  196. if khe != nil || key_hash_len <= 4 {
  197. glog.V(0).Infoln("Invalid key_hash", key_hash_string, "length:", key_hash_len, "error", khe)
  198. return 0, 0, errors.New("Invalid key and hash:" + key_hash_string)
  199. }
  200. key := util.BytesToUint64(key_hash_bytes[0 : key_hash_len-4])
  201. hash := util.BytesToUint32(key_hash_bytes[key_hash_len-4 : key_hash_len])
  202. return key, hash, nil
  203. }