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.

236 lines
5.3 KiB

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