No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

291 líneas
7.7KB

  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" || strings.ToLower(m.Upload.Format) ==
  187. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" {
  188. return "excel", nil
  189. }
  190. if strings.ToLower(strMime) == "application/zip" &&
  191. strings.ToLower(m.Upload.Format) ==
  192. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" {
  193. return "opensheet", nil
  194. }
  195. // check suffix which is not reliable
  196. ext := filepath.Ext(m.Upload.FileName)
  197. ext = strings.ToLower(ext)
  198. if ext[0] == '.' {
  199. ext = ext[1:] // remove the first char 'dot' .
  200. }
  201. switch ext {
  202. case "xls", "xlsx":
  203. return "excel", nil
  204. case "pdf":
  205. return "pdf", nil
  206. default:
  207. log.Warn("unhandled uploads type", ext)
  208. }
  209. return "", nil
  210. }
  211. // tested, not accurate with xls, xlsx, it becomes zip and octstream sometime.
  212. func GetFileContentType(filename string) (contentType string, e error) {
  213. contentType = ""
  214. input, e := os.OpenFile(filename, os.O_RDONLY, 0755)
  215. // Only the first 512 bytes are used to sniff the content type.
  216. buffer := make([]byte, 512)
  217. _, e = input.Read(buffer)
  218. if e != nil {
  219. return
  220. }
  221. // Use the net/http package's handy DectectContentType function. Always returns a valid
  222. // content-type by returning "application/octet-stream" if no others seemed to match.
  223. contentType = http.DetectContentType(buffer)
  224. return
  225. }
  226. func ConvertImageToThumbnail(srcPath string, dstPath string, size int) (e error) {
  227. if fileExists(dstPath) {
  228. log.Info("skip converting thumbnail it exists", dstPath)
  229. return
  230. }
  231. if size <= 0 {
  232. size = 256
  233. }
  234. // convert -thumbnail 200 abc.png thumb.abc.png
  235. cmd := exec.Command("convert", "-thumbnail", strconv.Itoa(size), srcPath, dstPath)
  236. strCmd := cmd.String()
  237. log.Debug("create thumbnail: ", strCmd)
  238. out, e := cmd.Output()
  239. if e != nil {
  240. return
  241. } else { // success
  242. log.Info("success output: \n: ", string(out))
  243. }
  244. return
  245. }
  246. func fileNameWithoutExtTrimSuffix(fileName string) string {
  247. return strings.TrimSuffix(fileName, filepath.Ext(fileName))
  248. }