修复: PDF导出浏览器探测补全与链路加固
- 浏览器探测补按用户安装路径LOCALAPPDATA - fs-agent远程HTML导出先拉取到本地临时目录 - Markdown通道空结果守卫,杜绝0字节PDF误报成功 - 清理死参数与无调用死代码
This commit is contained in:
-10
@@ -76,13 +76,3 @@ func (a *App) ExportHtmlFileToPDF(path string, saveName string) (map[string]inte
|
|||||||
"size": result.Size,
|
"size": result.Size,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectPDFSaveDirectory 选择PDF保存目录
|
|
||||||
func (a *App) SelectPDFSaveDirectory() (string, error) {
|
|
||||||
pdfAPI, err := a.requirePdfAPI()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return pdfAPI.SelectDirectory()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { FsTransport, FileItem, FileOperationResult, DetectTypeResult } from './transport'
|
import type { FsTransport, FileItem, FileOperationResult, DetectTypeResult } from './transport'
|
||||||
|
import { GetEnvVars, WriteFile } from '@bindings/u-desk/app'
|
||||||
|
|
||||||
const CONTENT_TYPE = 'application/json'
|
const CONTENT_TYPE = 'application/json'
|
||||||
|
|
||||||
@@ -156,7 +157,19 @@ export class HttpTransport implements FsTransport {
|
|||||||
|
|
||||||
async emptyRecycleBin(): Promise<void> {}
|
async emptyRecycleBin(): Promise<void> {}
|
||||||
|
|
||||||
|
// HTML 的 PDF 导出与本地预览都依赖本地真实文件(导出在后端 os.Stat,预览走本地文件服务器),
|
||||||
|
// 因此把远端 HTML 文本拉到本机临时目录;其余类型保持返回原路径走 fs-agent 代理流式预览,
|
||||||
|
// 二进制内容若也走 read 接口会被当文本损坏
|
||||||
async downloadForPreview(path: string): Promise<string> {
|
async downloadForPreview(path: string): Promise<string> {
|
||||||
return path
|
if (!/\.html?$/i.test(path)) return path
|
||||||
|
const content = await this.readFile(path)
|
||||||
|
const env = await GetEnvVars()
|
||||||
|
const tempDir = env['TEMP'] || env['TMP'] || env['TMPDIR'] || ''
|
||||||
|
if (!tempDir) return path
|
||||||
|
const sep = tempDir.includes('\\') ? '\\' : '/'
|
||||||
|
const base = path.split(/[\\/]/).pop() || 'preview.html'
|
||||||
|
const localPath = `${tempDir.replace(/[\\/]+$/, '')}${sep}udesk-http-${Date.now()}-${base}`
|
||||||
|
await WriteFile({ path: localPath, content })
|
||||||
|
return localPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export interface FsTransport {
|
|||||||
emptyRecycleBin(): Promise<void>
|
emptyRecycleBin(): Promise<void>
|
||||||
|
|
||||||
// 预览辅助
|
// 预览辅助
|
||||||
/** 下载远程文件到本地临时目录,本地/HTTP 直接返回原路径 */
|
/** 下载远程文件到本地临时目录,本地直接返回原路径;HTTP 仅对 HTML 落本地副本,其余类型返回原路径 */
|
||||||
downloadForPreview(path: string): Promise<string>
|
downloadForPreview(path: string): Promise<string>
|
||||||
/** 下载 HTML 及其引用的资源到临时目录(OSS 实现) */
|
/** 下载 HTML 及其引用的资源到临时目录(OSS 实现) */
|
||||||
downloadSiteForPreview?(path: string): Promise<string>
|
downloadSiteForPreview?(path: string): Promise<string>
|
||||||
|
|||||||
+8
-12
@@ -73,7 +73,7 @@ func (api *PdfAPI) ExportMarkdownToPDF(req PdfExportRequest) (*PdfExportResponse
|
|||||||
htmlContent := api.markdownToHTML(req.Content, req.Title, req.FontSize)
|
htmlContent := api.markdownToHTML(req.Content, req.Title, req.FontSize)
|
||||||
|
|
||||||
// 使用chromedp生成PDF
|
// 使用chromedp生成PDF
|
||||||
pdfBuffer, err := api.generatePDFFromHTML(htmlContent, req.Title, req.PageWidth, req.PageHeight)
|
pdfBuffer, err := api.generatePDFFromHTML(htmlContent, req.PageWidth, req.PageHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("生成PDF失败: %v", err)
|
return nil, fmt.Errorf("生成PDF失败: %v", err)
|
||||||
}
|
}
|
||||||
@@ -264,7 +264,7 @@ func (api *PdfAPI) markdownToHTML(markdownContent string, title string, fontSize
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generatePDFFromHTML 使用chromedp从HTML生成PDF
|
// generatePDFFromHTML 使用chromedp从HTML生成PDF
|
||||||
func (api *PdfAPI) generatePDFFromHTML(htmlContent, title string, pageWidth, pageHeight int) ([]byte, error) {
|
func (api *PdfAPI) generatePDFFromHTML(htmlContent string, pageWidth, pageHeight int) ([]byte, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
// 配置chromedp选项
|
// 配置chromedp选项
|
||||||
@@ -338,6 +338,10 @@ func (api *PdfAPI) generatePDFFromHTML(htmlContent, title string, pageWidth, pag
|
|||||||
return nil, fmt.Errorf("chromedp执行失败: %v", err)
|
return nil, fmt.Errorf("chromedp执行失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return nil, fmt.Errorf("PDF生成结果为空")
|
||||||
|
}
|
||||||
|
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,6 +354,8 @@ func findChromeExecutable() (string, error) {
|
|||||||
filepath.Join(os.Getenv("USERPROFILE"), "AppData", "Local", "Google", "Chrome", "Application", "chrome.exe"),
|
filepath.Join(os.Getenv("USERPROFILE"), "AppData", "Local", "Google", "Chrome", "Application", "chrome.exe"),
|
||||||
`C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe`,
|
`C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe`,
|
||||||
`C:\Program Files\Microsoft\Edge\Application\msedge.exe`,
|
`C:\Program Files\Microsoft\Edge\Application\msedge.exe`,
|
||||||
|
// 按用户安装的 Edge 不落在 Program Files,LOCALAPPDATA 下才有
|
||||||
|
filepath.Join(os.Getenv("LOCALAPPDATA"), "Microsoft", "Edge", "Application", "msedge.exe"),
|
||||||
}
|
}
|
||||||
for _, path := range candidates {
|
for _, path := range candidates {
|
||||||
if _, err := os.Stat(path); err == nil {
|
if _, err := os.Stat(path); err == nil {
|
||||||
@@ -520,14 +526,4 @@ func (api *PdfAPI) getDesktopDirectory() string {
|
|||||||
|
|
||||||
// 备用:当前目录
|
// 备用:当前目录
|
||||||
return "."
|
return "."
|
||||||
}
|
|
||||||
|
|
||||||
// SelectDirectory 选择保存目录(简化版,实际应该使用Wails runtime)
|
|
||||||
func (api *PdfAPI) SelectDirectory() (string, error) {
|
|
||||||
// 简化版:直接返回桌面目录
|
|
||||||
desktop := api.getDesktopDirectory()
|
|
||||||
if desktop == "." {
|
|
||||||
return "", fmt.Errorf("无法确定默认目录")
|
|
||||||
}
|
|
||||||
return desktop, nil
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user