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.

276 lines
7.2KB

  1. package main
  2. import (
  3. "biukop.com/sfm/loan"
  4. "errors"
  5. log "github.com/sirupsen/logrus"
  6. "io/ioutil"
  7. "net/http"
  8. "os"
  9. "os/exec"
  10. "path/filepath"
  11. "strconv"
  12. "strings"
  13. )
  14. type uploadsOnDisk struct {
  15. Upload loan.Uploads
  16. }
  17. func (m *uploadsOnDisk) convertUploadsToPDF() (e error) {
  18. return convertUploadsToPDF(m.Upload)
  19. }
  20. func convertUploadsToPDF(ul loan.Uploads) (e error) {
  21. m := uploadsOnDisk{}
  22. m.Upload = ul
  23. if strings.Contains(strings.ToLower(ul.Format), "excel") ||
  24. strings.Contains(strings.ToLower(ul.Format), "spreadsheet") {
  25. e = m.convertExcelToPDF()
  26. return // excel is converted
  27. }
  28. if strings.Contains(strings.ToLower(ul.Format), "/pdf") {
  29. return // no need to convert
  30. }
  31. e = errors.New("don't know how to convert file to PDF")
  32. log.Error("don't know how to convert file to PDF", ul)
  33. return
  34. }
  35. func (m *uploadsOnDisk) convertUploadsToJpg() (e error) {
  36. return convertUploadsToJpg(m.Upload)
  37. }
  38. func convertUploadsToJpg(ul loan.Uploads) (e error) {
  39. m := uploadsOnDisk{}
  40. m.Upload = ul
  41. if strings.Contains(strings.ToLower(ul.Format), "excel") ||
  42. strings.Contains(strings.ToLower(ul.Format), "spreadsheet") {
  43. e = m.convertExcelToJpg()
  44. return // excel is converted
  45. }
  46. if strings.Contains(strings.ToLower(ul.Format), "/pdf") {
  47. e = m.convertPDFToJpg()
  48. return // excel is converted
  49. }
  50. e = errors.New("don't know how to convert file to image")
  51. log.Error("don't know how to convert file to image", ul)
  52. return
  53. }
  54. func (m *uploadsOnDisk) convertUploadsToThumb() (e error) {
  55. return convertUploadsToThumb(m.Upload)
  56. }
  57. func convertUploadsToThumb(ul loan.Uploads) (e error) {
  58. m := uploadsOnDisk{}
  59. m.Upload = ul
  60. if !fileExists(m.jpgPath()) {
  61. e = m.convertUploadsToJpg()
  62. if e != nil {
  63. return
  64. }
  65. }
  66. e = ConvertImageToThumbnail(m.jpgPath(), m.thumbPath(), 256)
  67. if e != nil {
  68. log.Error("cannot create thumbnail for uploads", m.Upload, e)
  69. return
  70. }
  71. return
  72. }
  73. func (m *uploadsOnDisk) filePath() string {
  74. return config.UploadsDir.FileDir + strconv.Itoa(int(m.Upload.Id)) + ".uploads"
  75. }
  76. func (m *uploadsOnDisk) jpgPath() string {
  77. return config.UploadsDir.JpgDir + strconv.Itoa(int(m.Upload.Id)) + ".jpg"
  78. }
  79. func (m *uploadsOnDisk) thumbPath() string {
  80. return config.UploadsDir.ThumbDir + strconv.Itoa(int(m.Upload.Id)) + ".webp"
  81. }
  82. func (m *uploadsOnDisk) pdfPath() string {
  83. return config.UploadsDir.PdfDir + strconv.Itoa(int(m.Upload.Id)) + ".pdf"
  84. }
  85. func (m *uploadsOnDisk) convertExcelToPDF() (e error) {
  86. if fileExists(m.pdfPath()) {
  87. log.Info("Skip conversion excel to PDF , already exists", m)
  88. return
  89. }
  90. return m.convertExcelTo("pdf")
  91. }
  92. func (m *uploadsOnDisk) convertExcelToJpg() (e error) {
  93. if fileExists(m.jpgPath()) {
  94. log.Info("Skip conversion excel to Jpg , already exists", m)
  95. return
  96. }
  97. return m.convertExcelTo("jpg")
  98. }
  99. func (m *uploadsOnDisk) convertExcelTo(format string) (e error) {
  100. if format != "pdf" && format != "jpg" {
  101. e = errors.New("unsupported format")
  102. return
  103. }
  104. dst := m.jpgPath()
  105. if format == "pdf" {
  106. dst = m.pdfPath()
  107. }
  108. dir, e := ioutil.TempDir(config.TempDir, "tmp-convert-xls-to-"+format+"-")
  109. if e != nil {
  110. log.Error("cannot create tmp dir for converting image", m.Upload, e)
  111. return
  112. }
  113. defer os.RemoveAll(dir)
  114. cmd := exec.Command("libreoffice", "--convert-to", format, "--outdir", dir, m.filePath())
  115. strCmd := cmd.String()
  116. log.Debug("command is ", strCmd)
  117. out, e := cmd.Output()
  118. if e != nil {
  119. log.Error("cannot converting Excel to "+format+":", m.Upload, e)
  120. return
  121. } else { // success
  122. log.Info("convert to "+format, m.Upload, " output: ", string(out))
  123. }
  124. _, name := filepath.Split(m.filePath())
  125. src := dir + string(os.PathSeparator) + fileNameWithoutExtTrimSuffix(name) + "." + format
  126. e = os.Rename(src, dst) // there should be only one jpg
  127. return
  128. }
  129. // first page to thumbnail
  130. // all page to single jpg
  131. func (m *uploadsOnDisk) convertPDFToJpg() (e error) {
  132. if fileExists(m.jpgPath()) {
  133. // no need to reconvert it again
  134. log.Info("PDF to JPG skipped it already exists ", m)
  135. return
  136. }
  137. dir, e := ioutil.TempDir(config.TempDir, "tmp-convert-pdf-to-jpg-")
  138. if e != nil {
  139. log.Error("cannot create tmp dir for converting image", m.Upload, e)
  140. return
  141. }
  142. defer os.RemoveAll(dir)
  143. // convert -density 3000 abc.pdf path/tmp/result.png
  144. // could be path/tmp/result-0, result-1, result-2, ... png
  145. target := dir + string(os.PathSeparator) + "result.jpg" //.jpg suffix is important
  146. cmd := exec.Command("convert", "-density", "300", m.filePath(), target)
  147. strCmd := cmd.String()
  148. log.Debug("command is ", strCmd)
  149. out, e := cmd.Output()
  150. if e != nil {
  151. log.Error("cannot create png file for PDF", m.Upload, e)
  152. return
  153. } else {
  154. log.Info("convert ", m.Upload, " output: ", string(out))
  155. }
  156. // montage -mode concatenate -tile 1x 30*png 30.jpg
  157. if fileExists(target) { // single file,
  158. e = os.Rename(target, m.jpgPath()) // there should be only one jpg
  159. _ = ConvertImageToThumbnail(target, m.thumbPath(), 256)
  160. } else { // multi-page, we have -0 -1 -2 -3 -4 files
  161. firstPage := dir + string(os.PathSeparator) + "result-0.jpg"
  162. _ = ConvertImageToThumbnail(firstPage, m.thumbPath(), 256)
  163. batch := dir + string(os.PathSeparator) + "result*jpg" // result* is important
  164. target = dir + string(os.PathSeparator) + "final.jpg" // .jpg suffix is important
  165. cmd = exec.Command("montage", "-mode", "concatenate", "-tile", "1x", batch, target)
  166. strCmd = cmd.String()
  167. log.Debug("command is ", strCmd)
  168. out, e = cmd.Output()
  169. if e != nil {
  170. return
  171. } else {
  172. log.Info("montage ", m, " output: ", string(out))
  173. }
  174. e = os.Rename(target, m.jpgPath()) // give combined file to target
  175. }
  176. return
  177. }
  178. func (m *uploadsOnDisk) GetFileType() (ret string, e error) {
  179. strMime, e := GetFileContentType(m.filePath())
  180. if e != nil {
  181. return
  182. }
  183. if strings.ToLower(strMime) == "application/pdf" {
  184. return "pdf", nil
  185. }
  186. if strings.ToLower(strMime) == "application/vnd.ms-excel" {
  187. return "excel", nil
  188. }
  189. if strings.ToLower(strMime) == "application/zip" &&
  190. strings.ToLower(m.Upload.Format) ==
  191. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" {
  192. return "opensheet", nil
  193. }
  194. return "", nil
  195. }
  196. // tested, not accurate with xls, xlsx, it becomes zip and octstream sometime.
  197. func GetFileContentType(filename string) (contentType string, e error) {
  198. contentType = ""
  199. input, e := os.OpenFile(filename, os.O_RDONLY, 0755)
  200. // Only the first 512 bytes are used to sniff the content type.
  201. buffer := make([]byte, 512)
  202. _, e = input.Read(buffer)
  203. if e != nil {
  204. return
  205. }
  206. // Use the net/http package's handy DectectContentType function. Always returns a valid
  207. // content-type by returning "application/octet-stream" if no others seemed to match.
  208. contentType = http.DetectContentType(buffer)
  209. return
  210. }
  211. func ConvertImageToThumbnail(srcPath string, dstPath string, size int) (e error) {
  212. if fileExists(dstPath) {
  213. log.Info("skip converting thumbnail it exists", dstPath)
  214. return
  215. }
  216. if size <= 0 {
  217. size = 256
  218. }
  219. // convert -thumbnail 200 abc.png thumb.abc.png
  220. cmd := exec.Command("convert", "-thumbnail", strconv.Itoa(size), srcPath, dstPath)
  221. strCmd := cmd.String()
  222. log.Debug("create thumbnail: ", strCmd)
  223. out, e := cmd.Output()
  224. if e != nil {
  225. return
  226. } else { // success
  227. log.Info("success output: \n: ", string(out))
  228. }
  229. return
  230. }
  231. func fileNameWithoutExtTrimSuffix(fileName string) string {
  232. return strings.TrimSuffix(fileName, filepath.Ext(fileName))
  233. }