package main import ( "biukop.com/sfm/loan" "errors" log "github.com/sirupsen/logrus" "io/ioutil" "net/http" "os" "os/exec" "path/filepath" "strconv" "strings" ) type uploadsOnDisk struct { Upload loan.Uploads } func (m *uploadsOnDisk) convertUploadsToPDF() (e error) { return convertUploadsToPDF(m.Upload) } func convertUploadsToPDF(ul loan.Uploads) (e error) { m := uploadsOnDisk{} m.Upload = ul if strings.Contains(strings.ToLower(ul.Format), "excel") || strings.Contains(strings.ToLower(ul.Format), "spreadsheet") { e = m.convertExcelToPDF() return // excel is converted } if strings.Contains(strings.ToLower(ul.Format), "/pdf") { return // no need to convert } e = errors.New("don't know how to convert file to PDF") log.Error("don't know how to convert file to PDF", ul) return } func (m *uploadsOnDisk) convertUploadsToJpg() (e error) { return convertUploadsToJpg(m.Upload) } func convertUploadsToJpg(ul loan.Uploads) (e error) { m := uploadsOnDisk{} m.Upload = ul if strings.Contains(strings.ToLower(ul.Format), "excel") || strings.Contains(strings.ToLower(ul.Format), "spreadsheet") { e = m.convertExcelToJpg() return // excel is converted } if strings.Contains(strings.ToLower(ul.Format), "/pdf") { e = m.convertPDFToJpg() return // excel is converted } e = errors.New("don't know how to convert file to image") log.Error("don't know how to convert file to image", ul) return } func (m *uploadsOnDisk) convertUploadsToThumb() (e error) { return convertUploadsToThumb(m.Upload) } func convertUploadsToThumb(ul loan.Uploads) (e error) { m := uploadsOnDisk{} m.Upload = ul if !fileExists(m.jpgPath()) { e = m.convertUploadsToJpg() if e != nil { return } } e = ConvertImageToThumbnail(m.jpgPath(), m.thumbPath(), 256) if e != nil { log.Error("cannot create thumbnail for uploads", m.Upload, e) return } return } func (m *uploadsOnDisk) filePath() string { return config.UploadsDir.FileDir + strconv.Itoa(int(m.Upload.Id)) + ".uploads" } func (m *uploadsOnDisk) jpgPath() string { return config.UploadsDir.JpgDir + strconv.Itoa(int(m.Upload.Id)) + ".jpg" } func (m *uploadsOnDisk) thumbPath() string { return config.UploadsDir.ThumbDir + strconv.Itoa(int(m.Upload.Id)) + ".webp" } func (m *uploadsOnDisk) pdfPath() string { return config.UploadsDir.PdfDir + strconv.Itoa(int(m.Upload.Id)) + ".pdf" } func (m *uploadsOnDisk) convertExcelToPDF() (e error) { if fileExists(m.pdfPath()) { log.Info("Skip conversion excel to PDF , already exists", m) return } return m.convertExcelTo("pdf") } func (m *uploadsOnDisk) convertExcelToJpg() (e error) { if fileExists(m.jpgPath()) { log.Info("Skip conversion excel to Jpg , already exists", m) return } return m.convertExcelTo("jpg") } func (m *uploadsOnDisk) convertExcelTo(format string) (e error) { if format != "pdf" && format != "jpg" { e = errors.New("unsupported format") return } dst := m.jpgPath() if format == "pdf" { dst = m.pdfPath() } dir, e := ioutil.TempDir(config.TempDir, "tmp-convert-xls-to-"+format+"-") if e != nil { log.Error("cannot create tmp dir for converting image", m.Upload, e) return } defer os.RemoveAll(dir) cmd := exec.Command("libreoffice", "--convert-to", format, "--outdir", dir, m.filePath()) strCmd := cmd.String() log.Debug("command is ", strCmd) out, e := cmd.Output() if e != nil { log.Error("cannot converting Excel to "+format+":", m.Upload, e) return } else { // success log.Info("convert to "+format, m.Upload, " output: ", string(out)) } _, name := filepath.Split(m.filePath()) src := dir + string(os.PathSeparator) + fileNameWithoutExtTrimSuffix(name) + "." + format e = os.Rename(src, dst) // there should be only one jpg return } // first page to thumbnail // all page to single jpg func (m *uploadsOnDisk) convertPDFToJpg() (e error) { if fileExists(m.jpgPath()) { // no need to reconvert it again log.Info("PDF to JPG skipped it already exists ", m) return } dir, e := ioutil.TempDir(config.TempDir, "tmp-convert-pdf-to-jpg-") if e != nil { log.Error("cannot create tmp dir for converting image", m.Upload, e) return } defer os.RemoveAll(dir) // convert -density 3000 abc.pdf path/tmp/result.png // could be path/tmp/result-0, result-1, result-2, ... png target := dir + string(os.PathSeparator) + "result.jpg" //.jpg suffix is important cmd := exec.Command("convert", "-density", "300", m.filePath(), target) strCmd := cmd.String() log.Debug("command is ", strCmd) out, e := cmd.Output() if e != nil { log.Error("cannot create png file for PDF", m.Upload, e) return } else { log.Info("convert ", m.Upload, " output: ", string(out)) } // montage -mode concatenate -tile 1x 30*png 30.jpg if fileExists(target) { // single file, e = os.Rename(target, m.jpgPath()) // there should be only one jpg _ = ConvertImageToThumbnail(target, m.thumbPath(), 256) } else { // multi-page, we have -0 -1 -2 -3 -4 files firstPage := dir + string(os.PathSeparator) + "result-0.jpg" _ = ConvertImageToThumbnail(firstPage, m.thumbPath(), 256) batch := dir + string(os.PathSeparator) + "result*jpg" // result* is important target = dir + string(os.PathSeparator) + "final.jpg" // .jpg suffix is important cmd = exec.Command("montage", "-mode", "concatenate", "-tile", "1x", batch, target) strCmd = cmd.String() log.Debug("command is ", strCmd) out, e = cmd.Output() if e != nil { return } else { log.Info("montage ", m, " output: ", string(out)) } e = os.Rename(target, m.jpgPath()) // give combined file to target } return } func (m *uploadsOnDisk) GetFileType() (ret string, e error) { strMime, e := GetFileContentType(m.filePath()) if e != nil { return } if strings.ToLower(strMime) == "application/pdf" { return "pdf", nil } if strings.ToLower(strMime) == "application/vnd.ms-excel" || strings.ToLower(m.Upload.Format) == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" { return "excel", nil } if strings.ToLower(strMime) == "application/zip" && strings.ToLower(m.Upload.Format) == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" { return "opensheet", nil } // check suffix which is not reliable ext := filepath.Ext(m.Upload.FileName) ext = strings.ToLower(ext) if ext[0] == '.' { ext = ext[1:] // remove the first char 'dot' . } switch ext { case "xls", "xlsx": return "excel", nil case "pdf": return "pdf", nil default: log.Warn("unhandled uploads type", ext) } return "", nil } // tested, not accurate with xls, xlsx, it becomes zip and octstream sometime. func GetFileContentType(filename string) (contentType string, e error) { contentType = "" input, e := os.OpenFile(filename, os.O_RDONLY, 0755) // Only the first 512 bytes are used to sniff the content type. buffer := make([]byte, 512) _, e = input.Read(buffer) if e != nil { return } // Use the net/http package's handy DectectContentType function. Always returns a valid // content-type by returning "application/octet-stream" if no others seemed to match. contentType = http.DetectContentType(buffer) return } func ConvertImageToThumbnail(srcPath string, dstPath string, size int) (e error) { if fileExists(dstPath) { log.Info("skip converting thumbnail it exists", dstPath) return } if size <= 0 { size = 256 } // convert -thumbnail 200 abc.png thumb.abc.png cmd := exec.Command("convert", "-thumbnail", strconv.Itoa(size), srcPath, dstPath) strCmd := cmd.String() log.Debug("create thumbnail: ", strCmd) out, e := cmd.Output() if e != nil { return } else { // success log.Info("success output: \n: ", string(out)) } return } func fileNameWithoutExtTrimSuffix(fileName string) string { return strings.TrimSuffix(fileName, filepath.Ext(fileName)) }