Skip to content

Commit 3510a96

Browse files
committed
feat: 统一的错误处理
1 parent dc798b0 commit 3510a96

12 files changed

Lines changed: 278 additions & 46 deletions

File tree

README.md

Lines changed: 117 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -327,19 +327,129 @@ fmt.Println(doc.Content)
327327

328328
## 错误处理
329329

330-
所有读取操作都返回 error,建议进行适当的错误处理:
330+
库提供了统一的错误封装和类型检查功能,方便进行精确的错误处理。
331+
332+
### 预定义错误类型
333+
334+
```go
335+
var (
336+
ErrUnsupportedFormat = errors.New("unsupported file format") // 不支持的文件格式
337+
ErrFileNotFound = errors.New("file not found") // 文件不存在
338+
ErrFileOpen = errors.New("failed to open file") // 无法打开文件
339+
ErrFileRead = errors.New("failed to read file") // 读取文件失败
340+
ErrFileParse = errors.New("failed to parse file") // 解析文件失败
341+
ErrInvalidFormat = errors.New("invalid file format") // 文件格式无效
342+
ErrEmptyFile = errors.New("file is empty") // 文件为空
343+
ErrSheetNotFound = errors.New("sheet not found") // 工作表不存在
344+
)
345+
```
346+
347+
### 基本错误处理
331348

332349
```go
333350
doc, err := docreader.ReadDocument("file.docx")
334351
if err != nil {
352+
log.Printf("读取文件失败: %v", err)
353+
return
354+
}
355+
```
356+
357+
### 使用错误类型检查
358+
359+
```go
360+
doc, err := docreader.ReadDocument("file.unknown")
361+
if err != nil {
362+
// 使用 errors.Is 进行错误类型判断
363+
if errors.Is(err, docreader.ErrUnsupportedFormat) {
364+
log.Println("不支持的文件格式")
365+
} else if errors.Is(err, docreader.ErrFileNotFound) {
366+
log.Println("文件不存在")
367+
} else if errors.Is(err, docreader.ErrFileOpen) {
368+
log.Println("无法打开文件")
369+
} else {
370+
log.Printf("其他错误: %v", err)
371+
}
372+
return
373+
}
374+
```
375+
376+
### 使用辅助函数
377+
378+
```go
379+
doc, err := docreader.ReadDocument("file.pdf")
380+
if err != nil {
381+
// 使用辅助函数进行错误检查
382+
if docreader.IsUnsupportedFormat(err) {
383+
log.Println("不支持的文件格式")
384+
} else if docreader.IsFileNotFound(err) {
385+
log.Println("文件不存在")
386+
} else if docreader.IsFileOpen(err) {
387+
log.Println("无法打开文件")
388+
} else if docreader.IsFileRead(err) {
389+
log.Println("读取文件失败")
390+
} else if docreader.IsFileParse(err) {
391+
log.Println("解析文件失败")
392+
} else {
393+
log.Printf("未知错误: %v", err)
394+
}
395+
return
396+
}
397+
```
398+
399+
### 获取详细错误信息
400+
401+
```go
402+
doc, err := docreader.ReadDocument("file.docx")
403+
if err != nil {
404+
// 错误信息包含操作名称和文件路径
405+
// 格式: "操作名称: 文件路径: 错误详情"
406+
log.Printf("详细错误: %v", err)
407+
408+
// 使用 errors.Unwrap 获取原始错误
409+
if unwrapped := errors.Unwrap(err); unwrapped != nil {
410+
log.Printf("原始错误: %v", unwrapped)
411+
}
412+
return
413+
}
414+
```
415+
416+
### 完整示例
417+
418+
```go
419+
package main
420+
421+
import (
422+
"errors"
423+
"log"
424+
"github.com/yourusername/docreader"
425+
)
426+
427+
func main() {
428+
filePath := "document.pdf"
429+
430+
doc, err := docreader.ReadDocument(filePath)
431+
if err != nil {
432+
handleError(err)
433+
return
434+
}
435+
436+
log.Printf("成功读取文档,内容长度: %d", len(doc.Content))
437+
}
438+
439+
func handleError(err error) {
335440
switch {
336-
case strings.Contains(err.Error(), "不支持的文件格式"):
337-
log.Println("文件格式不支持")
338-
case strings.Contains(err.Error(), "无法打开"):
339-
log.Println("文件不存在或无法访问")
441+
case docreader.IsUnsupportedFormat(err):
442+
log.Println("错误: 不支持的文件格式,请使用 .docx, .pdf, .xlsx, .pptx, .txt, .csv, .md 或 .rtf 格式")
443+
case docreader.IsFileNotFound(err):
444+
log.Println("错误: 文件不存在,请检查文件路径")
445+
case docreader.IsFileOpen(err):
446+
log.Println("错误: 无法打开文件,请检查文件权限")
447+
case docreader.IsFileRead(err):
448+
log.Println("错误: 读取文件失败,文件可能已损坏")
449+
case docreader.IsFileParse(err):
450+
log.Println("错误: 解析文件失败,文件格式可能不正确")
340451
default:
341-
log.Printf("读取失败: %v", err)
452+
log.Printf("错误: %v", err)
342453
}
343-
return
344454
}
345455
```

csv.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func (r *CsvReader) ReadText(filePath string) (string, error) {
1515
// 打开文件
1616
file, err := os.Open(filePath)
1717
if err != nil {
18-
return "", fmt.Errorf("failed to open csv file: %w", err)
18+
return "", WrapError("CsvReader.ReadText", filePath, ErrFileOpen)
1919
}
2020
defer file.Close()
2121

@@ -25,7 +25,7 @@ func (r *CsvReader) ReadText(filePath string) (string, error) {
2525
// 读取所有记录
2626
records, err := reader.ReadAll()
2727
if err != nil {
28-
return "", fmt.Errorf("failed to read csv content: %w", err)
28+
return "", WrapError("CsvReader.ReadText", filePath, ErrFileRead)
2929
}
3030

3131
var builder strings.Builder
@@ -47,7 +47,7 @@ func (r *CsvReader) GetMetadata(filePath string) (map[string]string, error) {
4747
// 打开文件
4848
file, err := os.Open(filePath)
4949
if err != nil {
50-
return nil, fmt.Errorf("failed to open csv file: %w", err)
50+
return nil, WrapError("CsvReader.GetMetadata", filePath, ErrFileOpen)
5151
}
5252
defer file.Close()
5353

@@ -57,7 +57,7 @@ func (r *CsvReader) GetMetadata(filePath string) (map[string]string, error) {
5757
// 读取所有记录
5858
records, err := reader.ReadAll()
5959
if err != nil {
60-
return nil, fmt.Errorf("failed to read csv content: %w", err)
60+
return nil, WrapError("CsvReader.GetMetadata", filePath, ErrFileRead)
6161
}
6262

6363
metadata["rows"] = fmt.Sprintf("%d", len(records))
@@ -79,14 +79,14 @@ func (r *CsvReader) GetMetadata(filePath string) (map[string]string, error) {
7979
func (r *CsvReader) GetRecords(filePath string) ([][]string, error) {
8080
file, err := os.Open(filePath)
8181
if err != nil {
82-
return nil, fmt.Errorf("failed to open csv file: %w", err)
82+
return nil, WrapError("CsvReader.GetRecords", filePath, ErrFileOpen)
8383
}
8484
defer file.Close()
8585

8686
reader := csv.NewReader(file)
8787
records, err := reader.ReadAll()
8888
if err != nil {
89-
return nil, fmt.Errorf("failed to read csv content: %w", err)
89+
return nil, WrapError("CsvReader.GetRecords", filePath, ErrFileRead)
9090
}
9191

9292
return records, nil

docx.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package docreader
33
import (
44
"archive/zip"
55
"encoding/xml"
6-
"fmt"
76
"io"
87
"strings"
98
)
@@ -50,7 +49,7 @@ func (r *DocxReader) ReadText(filePath string) (string, error) {
5049
// 打开 zip 文件
5150
zipReader, err := zip.OpenReader(filePath)
5251
if err != nil {
53-
return "", fmt.Errorf("failed to open docx file: %w", err)
52+
return "", WrapError("DocxReader.ReadText", filePath, ErrFileOpen)
5453
}
5554
defer zipReader.Close()
5655

@@ -60,25 +59,25 @@ func (r *DocxReader) ReadText(filePath string) (string, error) {
6059
if file.Name == "word/document.xml" {
6160
rc, err := file.Open()
6261
if err != nil {
63-
return "", fmt.Errorf("failed to open document.xml: %w", err)
62+
return "", WrapError("DocxReader.ReadText", filePath, ErrFileRead)
6463
}
6564
documentXML, err = io.ReadAll(rc)
6665
rc.Close()
6766
if err != nil {
68-
return "", fmt.Errorf("failed to read document.xml: %w", err)
67+
return "", WrapError("DocxReader.ReadText", filePath, ErrFileRead)
6968
}
7069
break
7170
}
7271
}
7372

7473
if documentXML == nil {
75-
return "", fmt.Errorf("document.xml not found")
74+
return "", WrapError("DocxReader.ReadText", filePath, ErrInvalidFormat)
7675
}
7776

7877
// 解析 XML
7978
var doc WordDocument
8079
if err := xml.Unmarshal(documentXML, &doc); err != nil {
81-
return "", fmt.Errorf("failed to parse XML: %w", err)
80+
return "", WrapError("DocxReader.ReadText", filePath, ErrFileParse)
8281
}
8382

8483
// 提取文本
@@ -115,7 +114,7 @@ func (r *DocxReader) ReadText(filePath string) (string, error) {
115114
func (r *DocxReader) GetMetadata(filePath string) (map[string]string, error) {
116115
zipReader, err := zip.OpenReader(filePath)
117116
if err != nil {
118-
return nil, fmt.Errorf("failed to open docx file: %w", err)
117+
return nil, WrapError("DocxReader.GetMetadata", filePath, ErrFileOpen)
119118
}
120119
defer zipReader.Close()
121120

errors.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package docreader
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
)
7+
8+
// 预定义的错误类型
9+
var (
10+
// ErrUnsupportedFormat 不支持的文件格式
11+
ErrUnsupportedFormat = errors.New("unsupported file format")
12+
13+
// ErrFileNotFound 文件不存在
14+
ErrFileNotFound = errors.New("file not found")
15+
16+
// ErrFileOpen 无法打开文件
17+
ErrFileOpen = errors.New("failed to open file")
18+
19+
// ErrFileRead 读取文件失败
20+
ErrFileRead = errors.New("failed to read file")
21+
22+
// ErrFileParse 解析文件失败
23+
ErrFileParse = errors.New("failed to parse file")
24+
25+
// ErrInvalidFormat 文件格式无效
26+
ErrInvalidFormat = errors.New("invalid file format")
27+
28+
// ErrEmptyFile 文件为空
29+
ErrEmptyFile = errors.New("file is empty")
30+
31+
// ErrSheetNotFound 工作表不存在
32+
ErrSheetNotFound = errors.New("sheet not found")
33+
)
34+
35+
// DocumentError 文档错误结构
36+
type DocumentError struct {
37+
Op string // 操作名称
38+
FilePath string // 文件路径
39+
Err error // 原始错误
40+
}
41+
42+
// Error 实现 error 接口
43+
func (e *DocumentError) Error() string {
44+
if e.FilePath != "" {
45+
return fmt.Sprintf("%s: %s: %v", e.Op, e.FilePath, e.Err)
46+
}
47+
return fmt.Sprintf("%s: %v", e.Op, e.Err)
48+
}
49+
50+
// Unwrap 返回原始错误,支持 errors.Is 和 errors.As
51+
func (e *DocumentError) Unwrap() error {
52+
return e.Err
53+
}
54+
55+
// NewError 创建新的文档错误
56+
func NewError(op, filePath string, err error) error {
57+
return &DocumentError{
58+
Op: op,
59+
FilePath: filePath,
60+
Err: err,
61+
}
62+
}
63+
64+
// WrapError 包装错误并添加上下文信息
65+
func WrapError(op, filePath string, err error) error {
66+
if err == nil {
67+
return nil
68+
}
69+
return NewError(op, filePath, err)
70+
}
71+
72+
// IsUnsupportedFormat 检查是否为不支持的格式错误
73+
func IsUnsupportedFormat(err error) bool {
74+
return errors.Is(err, ErrUnsupportedFormat)
75+
}
76+
77+
// IsFileNotFound 检查是否为文件不存在错误
78+
func IsFileNotFound(err error) bool {
79+
return errors.Is(err, ErrFileNotFound)
80+
}
81+
82+
// IsFileOpen 检查是否为文件打开错误
83+
func IsFileOpen(err error) bool {
84+
return errors.Is(err, ErrFileOpen)
85+
}
86+
87+
// IsFileRead 检查是否为文件读取错误
88+
func IsFileRead(err error) bool {
89+
return errors.Is(err, ErrFileRead)
90+
}
91+
92+
// IsFileParse 检查是否为文件解析错误
93+
func IsFileParse(err error) bool {
94+
return errors.Is(err, ErrFileParse)
95+
}

md.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func (r *MdReader) ReadText(filePath string) (string, error) {
1313
// 读取文件内容
1414
data, err := os.ReadFile(filePath)
1515
if err != nil {
16-
return "", fmt.Errorf("failed to read markdown file: %w", err)
16+
return "", WrapError("MdReader.ReadText", filePath, ErrFileRead)
1717
}
1818

1919
return string(data), nil
@@ -26,7 +26,7 @@ func (r *MdReader) GetMetadata(filePath string) (map[string]string, error) {
2626
// 获取文件信息
2727
fileInfo, err := os.Stat(filePath)
2828
if err != nil {
29-
return nil, fmt.Errorf("failed to get file info: %w", err)
29+
return nil, WrapError("MdReader.GetMetadata", filePath, ErrFileNotFound)
3030
}
3131

3232
metadata["size"] = fmt.Sprintf("%d", fileInfo.Size())

pdf.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func (r *PdfReader) ReadText(filePath string) (string, error) {
1414
// 打开 PDF 文件
1515
f, reader, err := pdf.Open(filePath)
1616
if err != nil {
17-
return "", fmt.Errorf("failed to open PDF file: %w", err)
17+
return "", WrapError("PdfReader.ReadText", filePath, ErrFileOpen)
1818
}
1919
defer f.Close()
2020

@@ -47,7 +47,7 @@ func (r *PdfReader) ReadText(filePath string) (string, error) {
4747
func (r *PdfReader) GetMetadata(filePath string) (map[string]string, error) {
4848
f, reader, err := pdf.Open(filePath)
4949
if err != nil {
50-
return nil, fmt.Errorf("failed to open PDF file: %w", err)
50+
return nil, WrapError("PdfReader.GetMetadata", filePath, ErrFileOpen)
5151
}
5252
defer f.Close()
5353

0 commit comments

Comments
 (0)