photo_cache_service.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // photo_cache_service
  2. // TODO:
  3. // - как-то сделать обновление в html картин после создания thumb к ним, видимо заменять весь блок div class col-
  4. package main
  5. import (
  6. "io/ioutil"
  7. "net/http"
  8. "os"
  9. "runtime"
  10. "strconv"
  11. "time"
  12. "github.com/hjson/hjson-go"
  13. "github.com/kpango/glg"
  14. )
  15. type ConfigStruct struct {
  16. PathImages string
  17. PathImagesLen int
  18. PathCacheThumb string
  19. PathCacheImages string
  20. PathWWW string
  21. DataIconLoading []byte
  22. DataIconLoadingName string
  23. DataIconFolder []byte
  24. DataIconFolderName string
  25. DataIconUnknown []byte
  26. DataIconUnknownName string
  27. Seconds2RefreshCache int
  28. Seconds2WaitAfterError int
  29. }
  30. var config ConfigStruct
  31. var list2CreateThumb []string // слайс для списка файлов на создание thumb
  32. var list2CreateThumbPriority []string // слайс для приоритетного списка файлов на создание thumb
  33. func main() {
  34. runtime.GOMAXPROCS(runtime.NumCPU())
  35. setConfig() // конфигурация
  36. // установки log-файла
  37. infolog := glg.FileWriter("photo_cache_service.log", 0666) // открытие лог.файла
  38. defer infolog.Close()
  39. // var customErrLevel glg.LEVEL
  40. customErrLevel := "FINE"
  41. // customErrLevel := "CRIT"
  42. // customErrLevel := "DEBUG"
  43. glg.Get().
  44. //SetMode(glg.BOTH). // default is STD
  45. AddWriter(infolog).
  46. SetWriter(infolog).
  47. SetLevelColor(glg.TagStringToLevel(customErrLevel), glg.Red) // set color output to user custom level
  48. // завершение установки log-файла
  49. glg.Infof("%s", "started")
  50. go updateCache()
  51. go thumbGoOverPriority()
  52. http.HandleFunc("/", ServeHTTP)
  53. err := http.ListenAndServe(":9090", nil)
  54. check(err)
  55. }
  56. func updateCache() {
  57. for {
  58. list2CreateThumb = list2CreateThumb[:0] // очистка списка на создания thumb
  59. glg.Infof("%s", "[listdir] started")
  60. listDir(config.PathImages, config.PathCacheThumb) // создание списка на создание thumb
  61. glg.Info("[thumb] start cache update")
  62. if thumbGoOver(0) { // запуск создания thumb
  63. glg.Infof("[thumb] cache updated")
  64. list2CreateThumb = list2CreateThumb[:0] // очистка списка на создания thumb
  65. glg.Infof("%s", "[listdirImg] started")
  66. listDir(config.PathImages, config.PathCacheImages) // создание списка на создание thumb
  67. glg.Info("[thumbImg] start cache update")
  68. if thumbGoOver(1) { // обновление кэша изображений
  69. glg.Infof("[thumbImg] image cache updated, wait %d second before rescan again", config.Seconds2RefreshCache)
  70. time.Sleep(time.Duration(config.Seconds2RefreshCache) * time.Second) // timeout, если все нормально
  71. } else {
  72. glg.Infof("[thumbImg] error, wait %d second before update cache again", config.Seconds2WaitAfterError)
  73. time.Sleep(time.Duration(config.Seconds2WaitAfterError) * time.Second)
  74. }
  75. } else {
  76. glg.Infof("[thumb] error, wait %d second before update cache again", config.Seconds2WaitAfterError)
  77. time.Sleep(time.Duration(config.Seconds2WaitAfterError) * time.Second)
  78. }
  79. }
  80. }
  81. // выводит содержимое папки, рекурсивная
  82. func listDir(baseDir string, cachePath string) {
  83. baseDirClean := baseDir[config.PathImagesLen:] // делаем относительный путь к фотографиям
  84. foldersCache, _ := ioutil.ReadDir(cachePath + baseDirClean) // чтение содержимого папки с кэшем
  85. folders, _ := ioutil.ReadDir(baseDir) // чтение содержимого папки с фотографиями
  86. refreshCache(folders, foldersCache, baseDirClean) // обновление кеша, удаление лишних файлов и папок
  87. for _, f := range folders { // перебор значений папки
  88. if f.Name()[0:1] != "." && f.Name()[0:1] != "@" { // пропускаем скрыте папки с . в начале имени
  89. if f.IsDir() == true {
  90. if !checkIfExist(cachePath + baseDirClean + f.Name()) { // если папка отсутствует в кеше, то создается
  91. os.Mkdir(cachePath+baseDirClean+f.Name(), 0644)
  92. }
  93. listDir(baseDir+f.Name()+`/`, cachePath) // если значение это папка, то вход внутрь
  94. } else {
  95. if !checkIfExist(cachePath+baseDirClean+f.Name()) && checkIfImg(f.Name()) { // если файл отсутствует в кеше, то создается
  96. list2CreateThumb = append(list2CreateThumb, baseDirClean+f.Name())
  97. }
  98. }
  99. }
  100. }
  101. }
  102. // проверка существует ли файл/папка
  103. func checkIfExist(path string) bool {
  104. if _, err := os.Stat(path); os.IsNotExist(err) {
  105. return false
  106. }
  107. return true
  108. }
  109. func checkIfExist4Log(path string) string {
  110. if _, err := os.Stat(path); os.IsNotExist(err) {
  111. return "- "
  112. }
  113. return "+ "
  114. }
  115. func check(e error) {
  116. if e != nil {
  117. panic(e)
  118. }
  119. }
  120. // актуализация кэша в указанной указанной папке
  121. func refreshCache(folders []os.FileInfo, foldersCache []os.FileInfo, baseDirClean string) {
  122. foldersH := make(map[string]string)
  123. foldersCacheH := make(map[string]string)
  124. // переводит содержимое папки в хэш
  125. for _, f := range folders {
  126. foldersH[f.Name()] = strconv.FormatBool(f.IsDir())
  127. }
  128. // переводит содержимое папки кеша в хэш
  129. for _, f := range foldersCache {
  130. foldersCacheH[f.Name()] = strconv.FormatBool(f.IsDir())
  131. }
  132. // сравнение папок с фотографиями и кэшом и выделение тех, которые в кеше лишние
  133. for k, v := range foldersCacheH {
  134. if foldersCacheH[k] != foldersH[k] { // если в кеше лишний файл/папка
  135. if v == "true" { // удалить, если папка
  136. os.RemoveAll(config.PathCacheThumb + baseDirClean + k)
  137. } else { // удалить, если файл
  138. os.Remove(config.PathCacheThumb + baseDirClean + k)
  139. }
  140. }
  141. }
  142. }
  143. func setConfig() {
  144. mainConfig := readCfgFile("config.json") // чтение конфигурации
  145. config.PathImages = mainConfig["PathImages"].(string) // только абсолютный путь со слэшем в конце
  146. config.PathImagesLen = len(config.PathImages)
  147. config.PathCacheThumb = mainConfig["PathCacheThumb"].(string) // со слэшем в конце
  148. config.PathCacheImages = mainConfig["PathCacheImages"].(string) // со слэшем в конце
  149. config.PathWWW = mainConfig["PathWWW"].(string) // путь до html, без слеша в конце
  150. config.DataIconFolderName = mainConfig["DataIconFolderName"].(string)
  151. config.DataIconLoadingName = mainConfig["DataIconLoadingName"].(string)
  152. config.DataIconUnknownName = mainConfig["DataIconUnknownName"].(string)
  153. config.DataIconFolder, _ = Asset(config.PathWWW + config.DataIconFolderName)
  154. config.DataIconLoading, _ = Asset(config.PathWWW + config.DataIconLoadingName)
  155. config.DataIconUnknown, _ = Asset(config.PathWWW + config.DataIconUnknownName)
  156. config.Seconds2RefreshCache = int(mainConfig["Seconds2RefreshCache"].(float64)) // пауза между актуализацией кэша
  157. config.Seconds2WaitAfterError = int(mainConfig["Seconds2WaitAfterError"].(float64)) // пауза после ошибки для повторного обновления кэша
  158. }
  159. // чтение конфиг файла
  160. func readCfgFile(filename string) map[string]interface{} {
  161. configFileText, err := ioutil.ReadFile(filename)
  162. if err != nil {
  163. panic(err)
  164. }
  165. var dat map[string]interface{}
  166. // Decode and a check for errors.
  167. if err := hjson.Unmarshal(configFileText, &dat); err != nil {
  168. panic(err)
  169. }
  170. return dat
  171. }