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.

219 lines
4.8 KiB

6 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. package operation
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "sort"
  9. "sync"
  10. "github.com/chrislusf/seaweedfs/weed/glog"
  11. "github.com/chrislusf/seaweedfs/weed/util"
  12. )
  13. var (
  14. // when the remote server does not allow range requests (Accept-Ranges was not set)
  15. ErrRangeRequestsNotSupported = errors.New("Range requests are not supported by the remote server")
  16. // ErrInvalidRange is returned by Read when trying to read past the end of the file
  17. ErrInvalidRange = errors.New("Invalid range")
  18. )
  19. type ChunkInfo struct {
  20. Fid string `json:"fid"`
  21. Offset int64 `json:"offset"`
  22. Size int64 `json:"size"`
  23. }
  24. type ChunkList []*ChunkInfo
  25. type ChunkManifest struct {
  26. Name string `json:"name,omitempty"`
  27. Mime string `json:"mime,omitempty"`
  28. Size int64 `json:"size,omitempty"`
  29. Chunks ChunkList `json:"chunks,omitempty"`
  30. }
  31. // seekable chunked file reader
  32. type ChunkedFileReader struct {
  33. Manifest *ChunkManifest
  34. Master string
  35. pos int64
  36. pr *io.PipeReader
  37. pw *io.PipeWriter
  38. mutex sync.Mutex
  39. }
  40. func (s ChunkList) Len() int { return len(s) }
  41. func (s ChunkList) Less(i, j int) bool { return s[i].Offset < s[j].Offset }
  42. func (s ChunkList) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  43. func LoadChunkManifest(buffer []byte, isGzipped bool) (*ChunkManifest, error) {
  44. if isGzipped {
  45. var err error
  46. if buffer, err = UnGzipData(buffer); err != nil {
  47. return nil, err
  48. }
  49. }
  50. cm := ChunkManifest{}
  51. if e := json.Unmarshal(buffer, &cm); e != nil {
  52. return nil, e
  53. }
  54. sort.Sort(cm.Chunks)
  55. return &cm, nil
  56. }
  57. func (cm *ChunkManifest) Marshal() ([]byte, error) {
  58. return json.Marshal(cm)
  59. }
  60. func (cm *ChunkManifest) DeleteChunks(master string) error {
  61. var fileIds []string
  62. for _, ci := range cm.Chunks {
  63. fileIds = append(fileIds, ci.Fid)
  64. }
  65. results, err := DeleteFiles(master, fileIds)
  66. if err != nil {
  67. glog.V(0).Infof("delete %+v: %v", fileIds, err)
  68. return fmt.Errorf("chunk delete: %v", err)
  69. }
  70. for _, result := range results {
  71. if result.Error != "" {
  72. glog.V(0).Infof("delete file %+v: %v", result.FileId, result.Error)
  73. return fmt.Errorf("chunk delete %v: %v", result.FileId, result.Error)
  74. }
  75. }
  76. return nil
  77. }
  78. func readChunkNeedle(fileUrl string, w io.Writer, offset int64) (written int64, e error) {
  79. req, err := http.NewRequest("GET", fileUrl, nil)
  80. if err != nil {
  81. return written, err
  82. }
  83. if offset > 0 {
  84. req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset))
  85. }
  86. resp, err := util.Do(req)
  87. if err != nil {
  88. return written, err
  89. }
  90. defer resp.Body.Close()
  91. switch resp.StatusCode {
  92. case http.StatusRequestedRangeNotSatisfiable:
  93. return written, ErrInvalidRange
  94. case http.StatusOK:
  95. if offset > 0 {
  96. return written, ErrRangeRequestsNotSupported
  97. }
  98. case http.StatusPartialContent:
  99. break
  100. default:
  101. return written, fmt.Errorf("Read chunk needle error: [%d] %s", resp.StatusCode, fileUrl)
  102. }
  103. return io.Copy(w, resp.Body)
  104. }
  105. func (cf *ChunkedFileReader) Seek(offset int64, whence int) (int64, error) {
  106. var err error
  107. switch whence {
  108. case 0:
  109. case 1:
  110. offset += cf.pos
  111. case 2:
  112. offset = cf.Manifest.Size - offset
  113. }
  114. if offset > cf.Manifest.Size {
  115. err = ErrInvalidRange
  116. }
  117. if cf.pos != offset {
  118. cf.Close()
  119. }
  120. cf.pos = offset
  121. return cf.pos, err
  122. }
  123. func (cf *ChunkedFileReader) WriteTo(w io.Writer) (n int64, err error) {
  124. cm := cf.Manifest
  125. chunkIndex := -1
  126. chunkStartOffset := int64(0)
  127. for i, ci := range cm.Chunks {
  128. if cf.pos >= ci.Offset && cf.pos < ci.Offset+ci.Size {
  129. chunkIndex = i
  130. chunkStartOffset = cf.pos - ci.Offset
  131. break
  132. }
  133. }
  134. if chunkIndex < 0 {
  135. return n, ErrInvalidRange
  136. }
  137. for ; chunkIndex < cm.Chunks.Len(); chunkIndex++ {
  138. ci := cm.Chunks[chunkIndex]
  139. // if we need read date from local volume server first?
  140. fileUrl, lookupError := LookupFileId(cf.Master, ci.Fid)
  141. if lookupError != nil {
  142. return n, lookupError
  143. }
  144. if wn, e := readChunkNeedle(fileUrl, w, chunkStartOffset); e != nil {
  145. return n, e
  146. } else {
  147. n += wn
  148. cf.pos += wn
  149. }
  150. chunkStartOffset = 0
  151. }
  152. return n, nil
  153. }
  154. func (cf *ChunkedFileReader) ReadAt(p []byte, off int64) (n int, err error) {
  155. cf.Seek(off, 0)
  156. return cf.Read(p)
  157. }
  158. func (cf *ChunkedFileReader) Read(p []byte) (int, error) {
  159. return cf.getPipeReader().Read(p)
  160. }
  161. func (cf *ChunkedFileReader) Close() (e error) {
  162. cf.mutex.Lock()
  163. defer cf.mutex.Unlock()
  164. return cf.closePipe()
  165. }
  166. func (cf *ChunkedFileReader) closePipe() (e error) {
  167. if cf.pr != nil {
  168. if err := cf.pr.Close(); err != nil {
  169. e = err
  170. }
  171. }
  172. cf.pr = nil
  173. if cf.pw != nil {
  174. if err := cf.pw.Close(); err != nil {
  175. e = err
  176. }
  177. }
  178. cf.pw = nil
  179. return e
  180. }
  181. func (cf *ChunkedFileReader) getPipeReader() io.Reader {
  182. cf.mutex.Lock()
  183. defer cf.mutex.Unlock()
  184. if cf.pr != nil && cf.pw != nil {
  185. return cf.pr
  186. }
  187. cf.closePipe()
  188. cf.pr, cf.pw = io.Pipe()
  189. go func(pw *io.PipeWriter) {
  190. _, e := cf.WriteTo(pw)
  191. pw.CloseWithError(e)
  192. }(cf.pw)
  193. return cf.pr
  194. }