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.

240 lines
5.8 KiB

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