package file_download import ( "archive/zip" "bytes" "encoding/xml" "errors" "fmt" "io" "net/http" "net/url" "path/filepath" "strings" "time" ) // 下载文件 func DownloadFile(url string, validFunc func(resp *http.Response) error) ([]byte, string, error) { // 设置超时 client := &http.Client{ Timeout: 30 * time.Second, } // 发送请求 resp, err := client.Get(url) if err != nil { return nil, "", err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, "", fmt.Errorf("HTTP %d: %s", resp.StatusCode, resp.Status) } if validFunc != nil { err = validFunc(resp) if err != nil { return nil, "", err } } // 读取文件数据 data, err := io.ReadAll(resp.Body) if err != nil { return nil, "", err } // 获取文件名 filename := getFilenameFromURL(url, resp) return data, filename, nil } // 从 URL 或响应头获取文件名 func getFilenameFromURL(urlStr string, resp *http.Response) string { // 1. 尝试从 Content-Disposition 头获取 contentDisposition := resp.Header.Get("Content-Disposition") if contentDisposition != "" { if strings.Contains(contentDisposition, "filename=") { parts := strings.Split(contentDisposition, "filename=") if len(parts) > 1 { filename := strings.Trim(parts[1], `"' `) return sanitizeFilename(filename) } } } // 2. 从 URL 路径获取 parsedURL, err := url.Parse(urlStr) if err == nil { path := parsedURL.Path if path != "" { filename := filepath.Base(path) if filename != "" && filename != "." && filename != "/" { return sanitizeFilename(filename) } } } // 3. 生成默认文件名 return fmt.Sprintf("word_%d.docx", time.Now().Unix()) } // 清理文件名 func sanitizeFilename(filename string) string { // 移除非法字符 illegalChars := []string{"/", "\\", ":", "*", "?", "\"", "<", ">", "|"} for _, char := range illegalChars { filename = strings.ReplaceAll(filename, char, "_") } // 确保有扩展名 if !strings.Contains(filename, ".") { filename += ".docx" } return filename } // 从URL获取Word文件的纯文本内容 func GetWordTextFromURL(url string, validFunc func(resp *http.Response) error) (string, string, error) { // 1. 下载文件 data, fileName, err := DownloadFile(url, validFunc) if err != nil { return "", "", fmt.Errorf("下载失败: %w", err) } // 2. 解析Word文件 text, err := parseWordContent(data) if err != nil { return "", "", fmt.Errorf("解析失败: %w", err) } return text, fileName, nil } // 解析Word内容 - 简单版本,只提取文字 func parseWordContent(data []byte) (string, error) { reader := bytes.NewReader(data) zipReader, err := zip.NewReader(reader, int64(len(data))) if err != nil { return "", fmt.Errorf("解压docx失败: %v", err) } var textBuilder strings.Builder // 遍历 ZIP 文件中的文件 for _, file := range zipReader.File { // 只处理文档主体文件 if file.Name == "word/document.xml" { rc, err := file.Open() if err != nil { return "", fmt.Errorf("打开文档文件失败: %v", err) } defer rc.Close() // 读取 XML 内容 xmlData, err := io.ReadAll(rc) if err != nil { return "", fmt.Errorf("读取XML失败: %v", err) } // 提取文本 text, err := parseWordXML(xmlData) if err != nil { return "", fmt.Errorf("解析XML失败: %v", err) } textBuilder.WriteString(text) break // 找到主文档后退出循环 } } return textBuilder.String(), nil } // 解析 Word XML 文档 func parseWordXML(xmlData []byte) (string, error) { type WordDocument struct { XMLName xml.Name `xml:"document"` Body struct { Paragraphs []struct { Runs []struct { Text string `xml:"t"` } `xml:"r"` } `xml:"p"` } `xml:"body"` } var doc WordDocument if err := xml.Unmarshal(xmlData, &doc); err != nil { // 尝试简化解析 return extractTextSimple(xmlData), nil } var textBuilder strings.Builder for _, para := range doc.Body.Paragraphs { for _, run := range para.Runs { textBuilder.WriteString(run.Text) } textBuilder.WriteString("\n") } return textBuilder.String(), nil } // 简化文本提取(处理更复杂的文档结构) func extractTextSimple(xmlData []byte) string { var textBuilder strings.Builder // 简单提取 标签内容 decoder := xml.NewDecoder(bytes.NewReader(xmlData)) for { token, err := decoder.Token() if err == io.EOF { break } if err != nil { continue } if startElem, ok := token.(xml.StartElement); ok { if startElem.Name.Local == "t" { // 读取文本内容 if nextToken, err := decoder.Token(); err == nil { if charData, ok := nextToken.(xml.CharData); ok { textBuilder.WriteString(string(charData)) } } } } } return textBuilder.String() } // 判断是否为 Word 文件 func IsWordFile(resp *http.Response) error { contentType := resp.Header.Get("Content-Type") wordContentTypes := []string{ "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.ms-word", "application/octet-stream", // 有些服务器可能返回这个 } contentType = strings.ToLower(contentType) for _, ct := range wordContentTypes { if strings.Contains(contentType, ct) { return nil } } return errors.New("错误的文件类型") }