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.

179 lines
4.6 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. "code.google.com/p/weed-fs/go/glog"
  4. "code.google.com/p/weed-fs/go/images"
  5. "code.google.com/p/weed-fs/go/util"
  6. "encoding/hex"
  7. "errors"
  8. "io/ioutil"
  9. "mime"
  10. "net/http"
  11. "path"
  12. "strconv"
  13. "strings"
  14. "time"
  15. )
  16. const (
  17. NeedleHeaderSize = 16 //should never change this
  18. NeedlePaddingSize = 8
  19. NeedleChecksumSize = 4
  20. MaxPossibleVolumeSize = 4 * 1024 * 1024 * 1024 * 8
  21. )
  22. /*
  23. * Needle file size is limited to 4GB for now.
  24. */
  25. type Needle struct {
  26. Cookie uint32 `comment:"random number to mitigate brute force lookups"`
  27. Id uint64 `comment:"needle id"`
  28. Size uint32 `comment:"sum of DataSize,Data,NameSize,Name,MimeSize,Mime"`
  29. DataSize uint32 `comment:"Data size"` //version2
  30. Data []byte `comment:"The actual file data"`
  31. Flags byte `comment:"boolean flags"` //version2
  32. NameSize uint8 //version2
  33. Name []byte `comment:"maximum 256 characters"` //version2
  34. MimeSize uint8 //version2
  35. Mime []byte `comment:"maximum 256 characters"` //version2
  36. LastModified uint64 //only store LastModifiedBytesLength bytes, which is 5 bytes to disk
  37. Ttl *TTL
  38. Checksum CRC `comment:"CRC32 to check integrity"`
  39. Padding []byte `comment:"Aligned to 8 bytes"`
  40. }
  41. func ParseUpload(r *http.Request) (fileName string, data []byte, mimeType string, isGzipped bool, modifiedTime uint64, ttl *TTL, e error) {
  42. form, fe := r.MultipartReader()
  43. if fe != nil {
  44. glog.V(0).Infoln("MultipartReader [ERROR]", fe)
  45. e = fe
  46. return
  47. }
  48. part, fe := form.NextPart()
  49. if fe != nil {
  50. glog.V(0).Infoln("Reading Multi part [ERROR]", fe)
  51. e = fe
  52. return
  53. }
  54. fileName = part.FileName()
  55. if fileName != "" {
  56. fileName = path.Base(fileName)
  57. }
  58. data, e = ioutil.ReadAll(part)
  59. if e != nil {
  60. glog.V(0).Infoln("Reading Content [ERROR]", e)
  61. return
  62. }
  63. dotIndex := strings.LastIndex(fileName, ".")
  64. ext, mtype := "", ""
  65. if dotIndex > 0 {
  66. ext = strings.ToLower(fileName[dotIndex:])
  67. mtype = mime.TypeByExtension(ext)
  68. }
  69. contentType := part.Header.Get("Content-Type")
  70. if contentType != "" && mtype != contentType {
  71. mimeType = contentType //only return mime type if not deductable
  72. mtype = contentType
  73. }
  74. if part.Header.Get("Content-Encoding") == "gzip" {
  75. isGzipped = true
  76. } else if IsGzippable(ext, mtype) {
  77. if data, e = GzipData(data); e != nil {
  78. return
  79. }
  80. isGzipped = true
  81. }
  82. if ext == ".gz" {
  83. isGzipped = true
  84. }
  85. if strings.HasSuffix(fileName, ".gz") {
  86. fileName = fileName[:len(fileName)-3]
  87. }
  88. modifiedTime, _ = strconv.ParseUint(r.FormValue("ts"), 10, 64)
  89. ttl, _ = ReadTTL(r.FormValue("ttl"))
  90. return
  91. }
  92. func NewNeedle(r *http.Request, fixJpgOrientation bool) (n *Needle, e error) {
  93. fname, mimeType, isGzipped := "", "", false
  94. n = new(Needle)
  95. fname, n.Data, mimeType, isGzipped, n.LastModified, n.Ttl, e = ParseUpload(r)
  96. if e != nil {
  97. return
  98. }
  99. if len(fname) < 256 {
  100. n.Name = []byte(fname)
  101. n.SetHasName()
  102. }
  103. if len(mimeType) < 256 {
  104. n.Mime = []byte(mimeType)
  105. n.SetHasMime()
  106. }
  107. if isGzipped {
  108. n.SetGzipped()
  109. }
  110. if n.LastModified == 0 {
  111. n.LastModified = uint64(time.Now().Unix())
  112. }
  113. n.SetHasLastModifiedDate()
  114. if n.Ttl != EMPTY_TTL {
  115. n.SetHasTtl()
  116. }
  117. if fixJpgOrientation {
  118. loweredName := strings.ToLower(fname)
  119. if mimeType == "image/jpeg" || strings.HasSuffix(loweredName, ".jpg") || strings.HasSuffix(loweredName, ".jpeg") {
  120. n.Data = images.FixJpgOrientation(n.Data)
  121. }
  122. }
  123. n.Checksum = NewCRC(n.Data)
  124. commaSep := strings.LastIndex(r.URL.Path, ",")
  125. dotSep := strings.LastIndex(r.URL.Path, ".")
  126. fid := r.URL.Path[commaSep+1:]
  127. if dotSep > 0 {
  128. fid = r.URL.Path[commaSep+1 : dotSep]
  129. }
  130. e = n.ParsePath(fid)
  131. return
  132. }
  133. func (n *Needle) ParsePath(fid string) (err error) {
  134. length := len(fid)
  135. if length <= 8 {
  136. return errors.New("Invalid fid:" + fid)
  137. }
  138. delta := ""
  139. deltaIndex := strings.LastIndex(fid, "_")
  140. if deltaIndex > 0 {
  141. fid, delta = fid[0:deltaIndex], fid[deltaIndex+1:]
  142. }
  143. n.Id, n.Cookie, err = ParseKeyHash(fid)
  144. if err != nil {
  145. return err
  146. }
  147. if delta != "" {
  148. if d, e := strconv.ParseUint(delta, 10, 64); e == nil {
  149. n.Id += d
  150. } else {
  151. return e
  152. }
  153. }
  154. return err
  155. }
  156. func ParseKeyHash(key_hash_string string) (uint64, uint32, error) {
  157. key_hash_bytes, khe := hex.DecodeString(key_hash_string)
  158. key_hash_len := len(key_hash_bytes)
  159. if khe != nil || key_hash_len <= 4 {
  160. glog.V(0).Infoln("Invalid key_hash", key_hash_string, "length:", key_hash_len, "error", khe)
  161. return 0, 0, errors.New("Invalid key and hash:" + key_hash_string)
  162. }
  163. key := util.BytesToUint64(key_hash_bytes[0 : key_hash_len-4])
  164. hash := util.BytesToUint32(key_hash_bytes[key_hash_len-4 : key_hash_len])
  165. return key, hash, nil
  166. }