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.

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