webserver.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "math"
  6. "mime"
  7. "net/http"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "strconv"
  12. "strings"
  13. "text/template"
  14. "github.com/kpango/glg"
  15. )
  16. type FolderStruct struct {
  17. Name string
  18. IsDir int
  19. NeedToRefresh bool
  20. }
  21. type NavStruct struct {
  22. Name string
  23. Href string
  24. Current bool
  25. }
  26. type PagesStruct struct {
  27. Name string
  28. Current bool
  29. }
  30. type IndexData struct {
  31. NavData []NavStruct
  32. FolderData []FolderStruct
  33. PagesData []PagesStruct
  34. ArrowPrev int
  35. ArrowNext int
  36. }
  37. func ServeHTTP(w http.ResponseWriter, r *http.Request) {
  38. // glg.Infof("[URL] %s", r.URL.Path) // путь обращения к вебсерверу
  39. if r.URL.Path == "/" {
  40. webShowFolder(w, r, config.PathImages)
  41. glg.Infof("[URL] %s", r.URL.Path) // путь обращения к вебсерверу
  42. return
  43. } else if checkIfInData, _ := AssetInfo(config.PathWWW + r.URL.Path); checkIfInData != nil { // если www файл существует
  44. data, _ := Asset(config.PathWWW + r.URL.Path)
  45. w.Header().Set("Content-Type", mime.TypeByExtension(filepath.Ext(r.URL.Path)))
  46. fmt.Fprintf(w, "%s", data)
  47. return
  48. } else if r.URL.Path == config.DataIconFolderName { // иконка папки
  49. fmt.Fprintf(w, "%s", config.DataIconFolder) // выводим содержимое файла
  50. return
  51. } else if r.URL.Path == config.DataIconUnknownName { // иконка необрабатываемого файла
  52. fmt.Fprintf(w, "%s", config.DataIconUnknown) // выводим содержимое файла
  53. return
  54. } else if checkIfImg(r.URL.Path) { // если это изображение
  55. if checkIfImageExist(r.URL.Path[1:]) { // если оно существует
  56. if isReqThumb(r) { // если запрашивается thumb
  57. if checkIfThumbInCache(r.URL.Path[1:]) { // если существует файл в кэше
  58. fmt.Fprintf(w, "%s", readFile(config.PathCacheThumb+r.URL.Path[1:])) // выводим содержимое файла
  59. } else {
  60. if len(list2CreateThumbPriority) < 30000 { // защита от переполнения памяти
  61. list2CreateThumbPriority = append(list2CreateThumbPriority, r.URL.Path[1:])
  62. }
  63. fmt.Fprintf(w, "%s", config.DataIconLoading) // выводим содержимое файла заглушки
  64. }
  65. } else if checkIfImgThumbInCache(r.URL.Path[1:]) { // если запрашивается изображение и он есть в кэше
  66. fmt.Fprintf(w, "%s", readFile(config.PathCacheImages+r.URL.Path[1:])) // выводим содержимое файла
  67. } else { // если запрашивается просто изображение
  68. fmt.Fprintf(w, "%s", readFile(config.PathImages+r.URL.Path[1:])) // выводим содержимое файла
  69. }
  70. } else { // вывод содержимого папки
  71. webShowFolder(w, r, config.PathImages)
  72. }
  73. return
  74. } else {
  75. webShowFolder(w, r, config.PathImages)
  76. glg.Infof("[URL] %s", r.URL.Path) // путь обращения к вебсерверу
  77. return
  78. }
  79. http.NotFound(w, r)
  80. return
  81. }
  82. func webShowFolder(w http.ResponseWriter, r *http.Request, baseDir string) {
  83. dataTemplateMain, _ := Asset(config.PathWWW + "/index.html") // загружаем основной шаблон
  84. funcMapEach := template.FuncMap{
  85. "each": func(interval, n int) bool {
  86. return (n+1)%interval == 0
  87. },
  88. }
  89. tMain, _ := template.New("main").Funcs(funcMapEach).Parse(fmt.Sprintf("%s", dataTemplateMain))
  90. url := webCleanPathInUrl(r) // очистка пути из url'а
  91. pathLevel := strings.Count(url, "/") // подсчет текущего уровня
  92. folder, _ := ioutil.ReadDir(baseDir + url) // чтение содержимого папки с фотографиями
  93. navData := webShowNavData(url, pathLevel) // формируем данные для навигации
  94. folderData := webShowFolderData(pathLevel, folder, r) // формируем данные для содержимого папки
  95. webShowFolderPageStart, webShowFolderPageFinish, webShowFolderPagePages, webShowFolderPageCurrent := webShowFolderPreparePages(r, 36, len(folderData)) // подготовка блока данных постраничного вывода
  96. pagesData := webShowFolderPreparePagesSlice(webShowFolderPageCurrent, webShowFolderPagePages) // подготовка slice'а для наполнения номерами страниц
  97. ArrowPrevInt, ArrowNextInt := webShowFolderPrepareArrows(webShowFolderPageCurrent, webShowFolderPagePages) // подготовка данных для стрелок листания страниц
  98. // обрезка выводимых данных до данных для текущей страницы
  99. folderData = folderData[webShowFolderPageStart:webShowFolderPageFinish]
  100. // вывод по шаблону
  101. tMain.Execute(w, IndexData{NavData: navData, FolderData: folderData, PagesData: pagesData, ArrowPrev: ArrowPrevInt, ArrowNext: ArrowNextInt})
  102. }
  103. // подготовка списка для навигации
  104. func webShowNavData(url string, pathLevel int) []NavStruct {
  105. navData := make([]NavStruct, 0) // подготовка slice'а для наполнения поля навигации
  106. var navDataFull string
  107. var navDataCurrent bool
  108. navUrl := strings.Split(url, "/")
  109. for i := 0; i < pathLevel; i++ {
  110. navDataFull = navDataFull + navUrl[i] + "/"
  111. if i == pathLevel-1 {
  112. navDataCurrent = true
  113. } else {
  114. navDataCurrent = false
  115. }
  116. navData = append(navData, NavStruct{Name: navUrl[i], Href: navDataFull, Current: navDataCurrent})
  117. }
  118. return navData
  119. }
  120. // подготовка списка содержимого папки
  121. func webShowFolderData(pathLevel int, folder []os.FileInfo, r *http.Request) []FolderStruct {
  122. folderData := make([]FolderStruct, 0) // подготовка slice'а для наполнения содержимым папки
  123. if pathLevel > 0 { // если корень папки
  124. folderData = append(folderData, FolderStruct{Name: "..", IsDir: 0, NeedToRefresh: false})
  125. }
  126. for _, v := range folder { // добавляем сначала только папки
  127. if v.IsDir() && v.Name()[0:1] != "." && v.Name()[0:1] != "@" {
  128. folderData = append(folderData, FolderStruct{Name: v.Name(), IsDir: 1, NeedToRefresh: false})
  129. }
  130. }
  131. for _, v := range folder { // добавляем уже файлы
  132. if !v.IsDir() && v.Name()[0:1] != "." && v.Name()[0:1] != "@" {
  133. if checkIfImg(r.URL.Path[:1] + v.Name()) { // если изображение
  134. folderData = append(folderData, FolderStruct{Name: v.Name(), IsDir: 2, NeedToRefresh: !checkIfThumbInCache(r.URL.Path[:1] + v.Name())})
  135. // } else { // если другой файл
  136. // folderData = append(folderData, FolderStruct{Name: v.Name(), IsDir: 3, NeedToRefresh: false})
  137. }
  138. }
  139. }
  140. return folderData
  141. }
  142. // подготовка slice'а для номеров страниц
  143. func webShowFolderPreparePagesSlice(webShowFolderPageCurrent int, webShowFolderPagePages int) []PagesStruct {
  144. pagesData := make([]PagesStruct, 0) // подготовка slice'а для наполнения номерами страниц
  145. if webShowFolderPagePages > 1 {
  146. for j := 0; j < webShowFolderPagePages; j++ { // создаем срез с номерами страниц и указанием текущей
  147. if j == (webShowFolderPageCurrent - 1) {
  148. pagesData = append(pagesData, PagesStruct{Name: strconv.Itoa(j + 1), Current: true}) // текущая страница
  149. } else {
  150. pagesData = append(pagesData, PagesStruct{Name: strconv.Itoa(j + 1), Current: false}) // все остальные страницы
  151. }
  152. }
  153. }
  154. return pagesData
  155. }
  156. // возвращает номера предыдущей и следующей страниц
  157. func webShowFolderPrepareArrows(webShowFolderPageCurrent int, webShowFolderPagePages int) (int, int) {
  158. var ArrowPrevInt, ArrowNextInt int
  159. if webShowFolderPageCurrent != 1 {
  160. ArrowPrevInt = webShowFolderPageCurrent - 1
  161. } else {
  162. ArrowPrevInt = 0
  163. }
  164. if webShowFolderPageCurrent < webShowFolderPagePages {
  165. ArrowNextInt = webShowFolderPageCurrent + 1
  166. } else {
  167. ArrowNextInt = 0
  168. }
  169. return ArrowPrevInt, ArrowNextInt
  170. }
  171. // подсчитываем начало, конец постраничного среза
  172. // на выходе: startPos, FinishPos, всего страниц (1+), текущая страница (1+)
  173. func webShowFolderPreparePages(r *http.Request, limit int, folderDataLen int) (int, int, int, int) {
  174. var webShowFolderPage, webShowFolderPageStart, webShowFolderPagePages, webShowFolderPageFinish int
  175. webShowFolderPagePages = calcPagesNumber(folderDataLen, limit)
  176. if _, ok := r.URL.Query()["p"]; ok { // если в URL передана переменная p
  177. webShowFolderPage = int(math.Abs(float64(str2int(strings.Join(r.URL.Query()["p"], ""))))) // берем целое число по модулю
  178. } else { // если нет переменной, то по умолчанию ставим первую страницы
  179. webShowFolderPage = 1
  180. }
  181. if webShowFolderPage > webShowFolderPagePages { // если переданный в url номер страницы больше общего количества страниц
  182. webShowFolderPage = 1
  183. }
  184. if (webShowFolderPage-1)*36 > folderDataLen || webShowFolderPage == 0 { // если переданная страницы превышает общее количество записей, то стартовая позиция равна первой странице
  185. webShowFolderPageStart = 0
  186. } else { // иначе расчитываем
  187. webShowFolderPageStart = (webShowFolderPage - 1) * 36
  188. }
  189. if webShowFolderPageStart+36 > folderDataLen { // если количество записей от начала старта превышает общее количество среза, то ограничиваемся максимальным размером среза
  190. webShowFolderPageFinish = folderDataLen
  191. } else { // иначе расчитываем
  192. webShowFolderPageFinish = webShowFolderPageStart + 36
  193. }
  194. return webShowFolderPageStart, webShowFolderPageFinish, webShowFolderPagePages, webShowFolderPage
  195. }
  196. // очищаем путь в URL
  197. func webCleanPathInUrl(r *http.Request) string {
  198. dir, _ := filepath.Split(r.URL.Path[1:])
  199. dir = path.Clean(dir)
  200. if dir[0:1] != "." { // еслли не корень папки, то добавляем слэш в конце
  201. dir = dir + "/"
  202. }
  203. return dir
  204. }
  205. // чтение содержимого файла
  206. func readFile(filename string) string {
  207. configFileText, err := ioutil.ReadFile(filename)
  208. if err != nil {
  209. panic(err)
  210. }
  211. str := string(configFileText)
  212. return str
  213. }
  214. // проверка является ли запрашиваемый файл изображением
  215. func checkIfImg(path string) bool {
  216. ext := filepath.Ext(path)
  217. imagesExt := make(map[string]bool)
  218. imagesExt[".jpg"] = true
  219. imagesExt[".jpeg"] = true
  220. imagesExt[".gif"] = true
  221. imagesExt[".png"] = true
  222. if _, ok := imagesExt[strings.ToLower(ext)]; ok {
  223. return true
  224. }
  225. return false
  226. }
  227. // если jpeg
  228. func checkIfJpeg(path string) bool {
  229. ext := filepath.Ext(path)
  230. imagesExt := make(map[string]bool)
  231. imagesExt[".jpg"] = true
  232. imagesExt[".jpeg"] = true
  233. if _, ok := imagesExt[strings.ToLower(ext)]; ok {
  234. return true
  235. }
  236. return false
  237. }
  238. // проверка существует ли изображение в кэше
  239. func checkIfThumbInCache(path string) bool {
  240. if _, err := os.Stat(config.PathCacheThumb + path); err == nil {
  241. return true
  242. }
  243. return false
  244. }
  245. // проверка существует ли изображение в кэше
  246. func checkIfImgThumbInCache(path string) bool {
  247. if _, err := os.Stat(config.PathCacheImages + path); err == nil {
  248. return true
  249. }
  250. return false
  251. }
  252. // проверка существует ли изображение на диске
  253. func checkIfImageExist(path string) bool {
  254. if _, err := os.Stat(config.PathImages + path); err == nil {
  255. return true
  256. }
  257. return false
  258. }
  259. // проверка запрашивается ли thumbnail
  260. func isReqThumb(r *http.Request) bool {
  261. if _, ok := r.URL.Query()["thumbnail"]; ok {
  262. return true
  263. }
  264. return false
  265. }
  266. // возвращает количество страниц
  267. func calcPagesNumber(summ int, perPage int) int {
  268. var pages int
  269. pages = summ / perPage
  270. if summ%perPage > 0 {
  271. pages = pages + 1
  272. }
  273. return pages
  274. }