word_parser.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package import_service
  2. import (
  3. "archive/zip"
  4. "encoding/xml"
  5. "fmt"
  6. "io"
  7. "path/filepath"
  8. "strings"
  9. )
  10. // WordParser Word 解析器(直接解析 .docx ZIP/XML)
  11. type WordParser struct{}
  12. // NewWordParser 创建 Word 解析器
  13. func NewWordParser() *WordParser {
  14. return &WordParser{}
  15. }
  16. // Supports 检查是否支持该文件
  17. func (p *WordParser) Supports(filePath string) bool {
  18. return strings.HasSuffix(strings.ToLower(filePath), ".docx") ||
  19. strings.HasSuffix(strings.ToLower(filePath), ".doc")
  20. }
  21. // Parse 解析 Word 文件(.docx 是 ZIP 压缩的 XML)
  22. func (p *WordParser) Parse(filePath string) (*ParsedDocument, error) {
  23. if strings.HasSuffix(strings.ToLower(filePath), ".doc") {
  24. return nil, fmt.Errorf("暂不支持旧版 .doc 格式,请转换为 .docx 后导入")
  25. }
  26. r, err := zip.OpenReader(filePath)
  27. if err != nil {
  28. return nil, fmt.Errorf("打开 Word 文档失败: %w", err)
  29. }
  30. defer r.Close()
  31. var docFile *zip.File
  32. for _, f := range r.File {
  33. if f.Name == "word/document.xml" {
  34. docFile = f
  35. break
  36. }
  37. }
  38. if docFile == nil {
  39. return nil, fmt.Errorf("Word 文档格式异常:找不到 word/document.xml")
  40. }
  41. rc, err := docFile.Open()
  42. if err != nil {
  43. return nil, fmt.Errorf("读取文档内容失败: %w", err)
  44. }
  45. defer rc.Close()
  46. data, err := io.ReadAll(rc)
  47. if err != nil {
  48. return nil, fmt.Errorf("读取文档内容失败: %w", err)
  49. }
  50. var doc struct {
  51. Body struct {
  52. Paragraphs []struct {
  53. Runs []struct {
  54. Text string `xml:"t"`
  55. } `xml:"r"`
  56. } `xml:"p"`
  57. } `xml:"body"`
  58. }
  59. if err := xml.Unmarshal(data, &doc); err != nil {
  60. return nil, fmt.Errorf("解析文档 XML 失败: %w", err)
  61. }
  62. var paragraphs []string
  63. for _, p := range doc.Body.Paragraphs {
  64. var texts []string
  65. for _, run := range p.Runs {
  66. if strings.TrimSpace(run.Text) != "" {
  67. texts = append(texts, run.Text)
  68. }
  69. }
  70. line := strings.TrimSpace(strings.Join(texts, ""))
  71. if line != "" {
  72. paragraphs = append(paragraphs, line)
  73. }
  74. }
  75. if len(paragraphs) == 0 {
  76. return nil, fmt.Errorf("Word 文档中未提取到文本内容")
  77. }
  78. content := strings.Join(paragraphs, "\n\n")
  79. title := paragraphs[0]
  80. if len([]rune(title)) > 100 {
  81. title = string([]rune(title)[:100])
  82. }
  83. if len([]rune(paragraphs[0])) > 100 {
  84. title = strings.TrimSuffix(filepath.Base(filePath), filepath.Ext(filePath))
  85. }
  86. return &ParsedDocument{
  87. Title: title,
  88. Content: content,
  89. Metadata: map[string]interface{}{"source": "word"},
  90. }, nil
  91. }