memzipfile.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package xweb
  2. import (
  3. "bytes"
  4. "compress/flate"
  5. "compress/gzip"
  6. "errors"
  7. "io"
  8. "io/ioutil"
  9. "net/http"
  10. "os"
  11. "strings"
  12. "sync"
  13. "time"
  14. )
  15. var gmfim map[string]*MemFileInfo = make(map[string]*MemFileInfo)
  16. var lock sync.RWMutex
  17. // OpenMemZipFile returns MemFile object with a compressed static file.
  18. // it's used for serve static file if gzip enable.
  19. func OpenMemZipFile(path string, zip string) (*MemFile, error) {
  20. osfile, e := os.Open(path)
  21. if e != nil {
  22. return nil, e
  23. }
  24. defer osfile.Close()
  25. osfileinfo, e := osfile.Stat()
  26. if e != nil {
  27. return nil, e
  28. }
  29. modtime := osfileinfo.ModTime()
  30. fileSize := osfileinfo.Size()
  31. lock.RLock()
  32. cfi, ok := gmfim[zip+":"+path]
  33. lock.RUnlock()
  34. if ok && cfi.ModTime() == modtime && cfi.fileSize == fileSize {
  35. } else {
  36. var content []byte
  37. if zip == "gzip" {
  38. //将文件内容压缩到zipbuf中
  39. var zipbuf bytes.Buffer
  40. gzipwriter, e := gzip.NewWriterLevel(&zipbuf, gzip.BestCompression)
  41. if e != nil {
  42. return nil, e
  43. }
  44. _, e = io.Copy(gzipwriter, osfile)
  45. gzipwriter.Close()
  46. if e != nil {
  47. return nil, e
  48. }
  49. //读zipbuf到content
  50. content, e = ioutil.ReadAll(&zipbuf)
  51. if e != nil {
  52. return nil, e
  53. }
  54. } else if zip == "deflate" {
  55. //将文件内容压缩到zipbuf中
  56. var zipbuf bytes.Buffer
  57. deflatewriter, e := flate.NewWriter(&zipbuf, flate.BestCompression)
  58. if e != nil {
  59. return nil, e
  60. }
  61. _, e = io.Copy(deflatewriter, osfile)
  62. deflatewriter.Close()
  63. if e != nil {
  64. return nil, e
  65. }
  66. //将zipbuf读入到content
  67. content, e = ioutil.ReadAll(&zipbuf)
  68. if e != nil {
  69. return nil, e
  70. }
  71. } else {
  72. content, e = ioutil.ReadAll(osfile)
  73. if e != nil {
  74. return nil, e
  75. }
  76. }
  77. cfi = &MemFileInfo{osfileinfo, modtime, content, int64(len(content)), fileSize}
  78. lock.Lock()
  79. defer lock.Unlock()
  80. gmfim[zip+":"+path] = cfi
  81. }
  82. return &MemFile{fi: cfi, offset: 0}, nil
  83. }
  84. // MemFileInfo contains a compressed file bytes and file information.
  85. // it implements os.FileInfo interface.
  86. type MemFileInfo struct {
  87. os.FileInfo
  88. modTime time.Time
  89. content []byte
  90. contentSize int64
  91. fileSize int64
  92. }
  93. // Name returns the compressed filename.
  94. func (fi *MemFileInfo) Name() string {
  95. return fi.Name()
  96. }
  97. // Size returns the raw file content size, not compressed size.
  98. func (fi *MemFileInfo) Size() int64 {
  99. return fi.contentSize
  100. }
  101. // Mode returns file mode.
  102. func (fi *MemFileInfo) Mode() os.FileMode {
  103. return fi.Mode()
  104. }
  105. // ModTime returns the last modified time of raw file.
  106. func (fi *MemFileInfo) ModTime() time.Time {
  107. return fi.modTime
  108. }
  109. // IsDir returns the compressing file is a directory or not.
  110. func (fi *MemFileInfo) IsDir() bool {
  111. return fi.IsDir()
  112. }
  113. // return nil. implement the os.FileInfo interface method.
  114. func (fi *MemFileInfo) Sys() interface{} {
  115. return nil
  116. }
  117. // MemFile contains MemFileInfo and bytes offset when reading.
  118. // it implements io.Reader,io.ReadCloser and io.Seeker.
  119. type MemFile struct {
  120. fi *MemFileInfo
  121. offset int64
  122. }
  123. // Close memfile.
  124. func (f *MemFile) Close() error {
  125. return nil
  126. }
  127. // Get os.FileInfo of memfile.
  128. func (f *MemFile) Stat() (os.FileInfo, error) {
  129. return f.fi, nil
  130. }
  131. // read os.FileInfo of files in directory of memfile.
  132. // it returns empty slice.
  133. func (f *MemFile) Readdir(count int) ([]os.FileInfo, error) {
  134. infos := []os.FileInfo{}
  135. return infos, nil
  136. }
  137. // Read bytes from the compressed file bytes.
  138. func (f *MemFile) Read(p []byte) (n int, err error) {
  139. if len(f.fi.content)-int(f.offset) >= len(p) {
  140. n = len(p)
  141. } else {
  142. n = len(f.fi.content) - int(f.offset)
  143. err = io.EOF
  144. }
  145. copy(p, f.fi.content[f.offset:f.offset+int64(n)])
  146. f.offset += int64(n)
  147. return
  148. }
  149. var errWhence = errors.New("Seek: invalid whence")
  150. var errOffset = errors.New("Seek: invalid offset")
  151. // Read bytes from the compressed file bytes by seeker.
  152. func (f *MemFile) Seek(offset int64, whence int) (ret int64, err error) {
  153. switch whence {
  154. default:
  155. return 0, errWhence
  156. case os.SEEK_SET:
  157. case os.SEEK_CUR:
  158. offset += f.offset
  159. case os.SEEK_END:
  160. offset += int64(len(f.fi.content))
  161. }
  162. if offset < 0 || int(offset) > len(f.fi.content) {
  163. return 0, errOffset
  164. }
  165. f.offset = offset
  166. return f.offset, nil
  167. }
  168. // GetAcceptEncodingZip returns accept encoding format in http header.
  169. // zip is first, then deflate if both accepted.
  170. // If no accepted, return empty string.
  171. func GetAcceptEncodingZip(r *http.Request) string {
  172. ss := r.Header.Get("Accept-Encoding")
  173. ss = strings.ToLower(ss)
  174. if strings.Contains(ss, "gzip") {
  175. return "gzip"
  176. } else if strings.Contains(ss, "deflate") {
  177. return "deflate"
  178. } else {
  179. return ""
  180. }
  181. return ""
  182. }
  183. // CloseZWriter closes the io.Writer after compressing static file.
  184. func CloseZWriter(zwriter io.Writer) {
  185. if zwriter == nil {
  186. return
  187. }
  188. switch zwriter.(type) {
  189. case *gzip.Writer:
  190. zwriter.(*gzip.Writer).Close()
  191. case *flate.Writer:
  192. zwriter.(*flate.Writer).Close()
  193. //其他情况不close, 保持和默认(非压缩)行为一致
  194. /*
  195. case io.WriteCloser:
  196. zwriter.(io.WriteCloser).Close()
  197. */
  198. }
  199. }