package pdf import ( "eta/eta_data_analysis/utils" "fmt" "os" "os/exec" "path" "strconv" "sync" ) const ( Mobile = "mobile" PC = "pc" FreeLayout = "freeLayout" ) var ( PdfParamsMap = map[string]PDFParams{ PC: { Top: 20, Bottom: 20, Left: 20, Right: 20, Width: 1200, }, Mobile: { Top: 20, Bottom: 20, Left: 20, Right: 20, Width: 600, }, FreeLayout: { Top: 0, Bottom: 0, Left: 0, Right: 0, Width: 1200, }, } pdfPathTemplate = "./static/%s_%d.pdf" jpegPathTemplate = "./static/%s_%d.jpg" ) type PDFParams struct { Top int Bottom int Left int Right int Width int } func uploadFile(ossClient utils.OssClient, filePath string, fileType string) (resourceUrl string, err error) { file, err := os.Open(filePath) if err != nil { utils.FileLog.Info("Open failed: , error: \n" + err.Error()) return } defer func() { _ = file.Close() }() ext := path.Ext(file.Name()) randStr := utils.GetRandStringNoSpecialChar(28) fileName := randStr + ext resourceUrl, err = ossClient.UploadFile(fileName, filePath, fileType) if err != nil { utils.FileLog.Info("文件上传失败, Err: \n" + err.Error()) return } defer func() { _ = os.Remove(filePath) }() return } func generateAndUploadPDF(params PDFParams, reportUrl, pdfPath string, ossClient utils.OssClient) (resourceUrl string, err error) { if reportUrl == "" { return } err = ReportToPdf(params.Width, reportUrl, pdfPath, params.Top, params.Bottom, params.Left, params.Right) if err != nil { utils.FileLog.Info("ReportToPdf failed: , error: \n" + err.Error()) return } resourceUrl, err = uploadFile(ossClient, pdfPath, "pdf") if err != nil { utils.FileLog.Info("文件上传失败, Err: \n" + err.Error()) return } return } func generateAndUploadJPEG(params PDFParams, reportUrl, pdfPath string, ossClient utils.OssClient) (resourceUrl string, err error) { if reportUrl == "" { return } err = ReportToJpeg(params.Width, reportUrl, pdfPath) if err != nil { utils.FileLog.Info("ReportToJpeg failed: , error: \n" + err.Error()) return } resourceUrl, err = uploadFile(ossClient, pdfPath, "jpg") if err != nil { utils.FileLog.Info("文件上传失败, Err: \n" + err.Error()) return } return } func Report2pdfAndJpeg(reportUrl string, reportId int, ReportLayout string) (pdfUrl, jpegUrl string, err error) { var params PDFParams if _, ok := PdfParamsMap[ReportLayout]; !ok { err = fmt.Errorf("报告类型 %s 不存在,生成PDF失败", ReportLayout) } else { params = PdfParamsMap[ReportLayout] } reportCode := utils.MD5(strconv.Itoa(reportId)) pdfPath := fmt.Sprintf(pdfPathTemplate, reportCode, params.Width) jpegPath := fmt.Sprintf(jpegPathTemplate, reportCode, params.Width) ossClient, err := utils.OssClientHandler(utils.OssType) if err != nil { utils.FileLog.Info("获取OSS客户端失败") err = fmt.Errorf("获取OSS客户端未初始化,生成PDF失败") } defer func() { if err != nil { utils.FileLog.Info("生成PDF失败,error: \n" + err.Error()) } }() // 并发执行 var wg sync.WaitGroup wg.Add(2) pdfChan := make(chan result, 1) jpegChan := make(chan result, 1) go func() { defer wg.Done() url, pdfErr := generateAndUploadPDF(params, reportUrl, pdfPath, ossClient) pdfChan <- result{url, pdfErr} }() go func() { defer wg.Done() url, jpgErr := generateAndUploadJPEG(params, reportUrl, jpegPath, ossClient) jpegChan <- result{url, jpgErr} }() wg.Wait() close(pdfChan) close(jpegChan) pdfResult := <-pdfChan jpegResult := <-jpegChan if pdfResult.err != nil || jpegResult.err != nil { utils.FileLog.Info("生成PDF失败,error: \n" + pdfResult.err.Error() + jpegResult.err.Error()) err = fmt.Errorf("生成PDF失败,error: \n" + pdfResult.err.Error() + jpegResult.err.Error()) return } pdfUrl, jpegUrl = pdfResult.url, jpegResult.url return } type result struct { url string err error } func ReportToPdf(width int, reportUrl, filePath string, top, bottom, left, right int) (err error) { pyCode := ` import asyncio from pyppeteer import launch @asyncio.coroutine async def main(): # 异步代码 browser = await launch({ 'executablePath': '%s', 'headless': True, 'args': ['--disable-infobars', '--no-sandbox'] }) page = await browser.newPage() await page.setViewport({ 'width': %d, 'height': 1697 }) await page.goto('%s', { 'waitUntil': 'networkidle0', 'timeout': 3000000 # 设置超时时间为 100 秒 }) # 在生成PDF之前等待2秒 await asyncio.sleep(15) await page.pdf({ 'width': %d, 'height': 1697, 'path': "%s", 'printBackground': True, 'margin': { 'top': '%dpx', 'bottom': '%dpx', 'left': '%dpx', 'right': '%dpx' } }) await browser.close() # 创建事件循环 loop = asyncio.get_event_loop() # 使用事件循环运行main函数 try: loop.run_until_complete(main()) finally: # 关闭事件循环 loop.close() ` pyCode = fmt.Sprintf(pyCode, utils.ChromePath, width, reportUrl, width+left+right, filePath, top, bottom, left, right) utils.FileLog.Info("pdf pyCode: \n" + pyCode) cmd := exec.Command(utils.CommandPython, "-c", pyCode) output, e := cmd.CombinedOutput() if e != nil { err = e fmt.Println("ReportToPdf failed: , error:" + string(output)) utils.FileLog.Info("ReportToPdf failed: , error: \n" + err.Error()) utils.FileLog.Info("Output: %s\n", string(output)) } defer func() { cmd.Process.Kill() }() return } func ReportToJpeg(width int, reportUrl, filePath string) (err error) { pyCode := ` import asyncio from pyppeteer import launch, errors async def main(): try: # 启动浏览器 browser = await launch({ 'executablePath': '%s', 'headless': True, 'args': ['--disable-infobars', '--no-sandbox'] }) # 新建页面 page = await browser.newPage() # 设置视口大小 await page.setViewport({ 'width': %d, 'height': 1697 }) # 导航到页面 await page.goto('%s', { 'waitUntil': 'networkidle0', 'timeout': 3000000 # 设置超时时间为 100 秒 }) # Customizing footer for page numbers starting from page 2 # 在这里添加两秒的等待 await asyncio.sleep(5) await page.screenshot({ 'path': "%s", 'fullPage': True, 'quality':80 }) except errors.BrowserError as e: print('Browser closed unexpectedly:', e) except Exception as e: print('An error occurred:', e) finally: # 确保浏览器关闭 if browser is not None: await browser.close() # 获取当前事件循环 loop = asyncio.get_event_loop() # 运行事件循环直到main协程完成 try: loop.run_until_complete(main()) except Exception as e: print('Error during event loop execution:', e) finally: # 关闭事件循环 loop.close() ` pyCode = fmt.Sprintf(pyCode, utils.ChromePath, width, reportUrl, filePath) utils.FileLog.Info("jpeg pyCode: \n" + pyCode) cmd := exec.Command(utils.CommandPython, "-c", pyCode) output, e := cmd.CombinedOutput() if e != nil { err = e utils.FileLog.Info("ReportToJpeg failed: , error: \n" + err.Error()) utils.FileLog.Info("Output: %s\n", string(output)) } defer func() { _ = cmd.Process.Kill() }() return }