首页 > 其他分享 >使用Go语言编写邮件内容解析功能

使用Go语言编写邮件内容解析功能

时间:2023-02-25 18:55:17浏览次数:31  
标签:return err nil msg Go 解析 email 邮件 string

保存为readmsg.go

package main

import (
    "bytes"
    "database/sql"
    "encoding/base64"
    "encoding/json"
    "io"
    "io/ioutil"
    "log"
    "mime"
    "mime/multipart"
    "net/mail"
    "strings"

    _ "github.com/go-sql-driver/mysql"
)

type Email struct {
    Id         int
    Created    string
    RemoteAddr string
    MailFrom   string
    RcptTo     string
    Data       []byte
    Header     string
    Subject    string
    BodyPlain  []byte
    BodyHtml   []byte
}

func main() {
    log.Println("connection...")
    db, err := sql.Open("mysql", "firadio_mail:firadio_mail@tcp(39.101.248.243:3306)/firadio_mail")
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()

    rows, err := db.Query("SELECT id,created,remote_addr,mail_from,rcpt_to,data FROM emails WHERE 1")
    if err != nil {
        panic(err.Error())
    }
    defer rows.Close()

    for rows.Next() {
        var email Email
        err := rows.Scan(&email.Id, &email.Created, &email.RemoteAddr, &email.MailFrom, &email.RcptTo, &email.Data)
        if err != nil {
            panic(err.Error())
        }
        //log.Println(email)
        err = saveMail(db, email)
        if err != nil {
            panic(err.Error())
        }
    }
    if err := rows.Err(); err != nil {
        panic(err.Error())
    }

}

func saveMail(db *sql.DB, email Email) error {
    // 解析并存储邮件
    log.Println("updating...", email.Id)

    // 准备插入语句
    stmt, err := db.Prepare("UPDATE emails SET header=?, subject=?, body_plain=?, body_html=? WHERE id=?")
    if err != nil {
        return err
    }
    defer stmt.Close()

    data := email.Data
    msg, err := mail.ReadMessage(bytes.NewReader(data))
    if err != nil {
        return err
    }

    if true {
        jsonHeader, err := GetJsonByMsg(msg)
        if err != nil {
            return err
        }
        //log.Println(jsonHeader)
        email.Header = jsonHeader
    }

    if true {
        subject, err := GetSubjectByMsg(msg)
        if err != nil {
            return err
        }
        //log.Println(subject)
        email.Subject = subject
    }

    // 一次性把MultipartBody部分全部读过来
    multipartBody, err := GetMultipartBody(msg)
    if err != nil {
        return err
    }

    if true {
        msg_body_plain, err := GetBodyByMsg(msg, multipartBody, "text/plain")
        if err != nil {
            return err
        }
        //log.Println("text/plain", string(msg_body_plain))
        email.BodyPlain = msg_body_plain
    }

    if true {
        msg_body_html, err := GetBodyByMsg(msg, multipartBody, "text/html")
        if err != nil {
            return err
        }
        //log.Println("text/html", string(msg_body_html))
        email.BodyHtml = msg_body_html
    }

    // 执行插入语句
    _, err = stmt.Exec(email.Header, email.Subject, email.BodyPlain, email.BodyHtml, email.Id)
    if err != nil {
        return err
    }

    return nil
}

func DecodeRFC2047String(s string) (string, error) {
    // 发信人,邮件主题可能需要解码
    dec := new(mime.WordDecoder)
    decoded, err := dec.DecodeHeader(s)
    if err != nil {
        return "", err
    }
    return decoded, nil
}

func GetSubjectByMsg(msg *mail.Message) (string, error) {
    // 读取Header中的邮件主题
    subject, err := DecodeRFC2047String(msg.Header.Get("Subject"))
    if err != nil {
        return "", err
    }
    return subject, nil
}

func GetJsonByMsg(msg *mail.Message) (string, error) {
    // 读取Header并转换为JSON
    for k, v := range msg.Header {
        for kk, vv := range v {
            vvv, err := DecodeRFC2047String(vv)
            if err != nil {
                continue
            }
            msg.Header[k][kk] = vvv
        }
    }
    sJson, err := json.Marshal(msg.Header)
    if err != nil {
        return "", err
    }
    return string(sJson), nil
}

func GetBodyByMsg(msg *mail.Message, multipartBody []MsgPartBody, ContentType string) ([]byte, error) {
    // 读取Data中的指定格式的内容,如果没有就返回空
    mediaType, _, err := mime.ParseMediaType(msg.Header.Get("Content-Type"))
    if err != nil {
        return nil, err
    }
    if mediaType == ContentType {
        bodyBytes, err := ioutil.ReadAll(msg.Body)
        if err != nil {
            return nil, err
        }
        if encoding := msg.Header.Get("Content-Transfer-Encoding"); encoding == "base64" {
            dst := make([]byte, base64.StdEncoding.DecodedLen(len(bodyBytes)))
            _, err := base64.StdEncoding.Decode(dst, bodyBytes)
            if err != nil {
                return nil, err
            }
            return dst, nil
        }
        return bodyBytes, nil
    }
    body, err := GetBodyByMsgPartBodys(multipartBody, ContentType)
    if err != nil {
        return nil, err
    }
    return body, nil
}

type MsgPartBody struct {
    ContentType string
    Body        []byte
}

func GetMultipartBody(msg *mail.Message) ([]MsgPartBody, error) {
    // 一次性取得需要的MultipartBody
    // 解析Multipart格式的邮件,并存入[]MsgPartBody数组
    bodys := []MsgPartBody{}
    mediaType, params, err := mime.ParseMediaType(msg.Header.Get("Content-Type"))
    if err != nil {
        return nil, err
    }
    if !strings.HasPrefix(mediaType, "multipart/") {
        return bodys, nil
    }
    mr := multipart.NewReader(msg.Body, params["boundary"])
    for {
        part, err := mr.NextPart()
        if err == io.EOF {
            break
        }
        if err != nil {
            return nil, err
        }
        partMediaType, _, err := mime.ParseMediaType(part.Header.Get("Content-Type"))
        if err != nil {
            continue
        }
        partBytes, err := ioutil.ReadAll(part)
        if err != nil {
            return nil, err
        }
        if encoding := part.Header.Get("Content-Transfer-Encoding"); encoding == "base64" {
            dst := make([]byte, base64.StdEncoding.DecodedLen(len(partBytes)))
            _, err := base64.StdEncoding.Decode(dst, partBytes)
            if err != nil {
                return nil, err
            }
            bodys = append(bodys, MsgPartBody{ContentType: partMediaType, Body: dst})
        }
        bodys = append(bodys, MsgPartBody{ContentType: partMediaType, Body: partBytes})

    }
    return bodys, nil
}

func GetBodyByMsgPartBodys(bodys []MsgPartBody, ContentType string) ([]byte, error) {
    // 从一次性取得并解析好的[]MsgPartBody获取邮件内容
    for _, v := range bodys {
        if v.ContentType == ContentType {
            return v.Body, nil
        }
    }
    return nil, nil
}

然后编译

标签:return,err,nil,msg,Go,解析,email,邮件,string
From: https://www.cnblogs.com/xiangxisheng/p/17155055.html

相关文章

  • Chrome插件:Chrome-Sync-Helper 使用google搜索
    解决墙的原因,无法使用google搜索找到插件 https://www.zhihu.com/question/387679779/answer/2822702411?utm_campaign=shareopn&utm_medium=social&utm_oi=7318067228......
  • 已解决(MongoDB安装报错)Service ‘MongoDB Server (MongoDB)’ (MongoDB) failed tosta
     报错问题粉丝群里面的一个小伙伴想安装MongoDB但是发生了报错(当时他心里瞬间凉了一大截,跑来找我求助,然后顺利帮助他解决了,顺便记录一下希望可以帮助到更多遇到这个bug......
  • Go从入门到精通——常见报错: C compiler "gcc" not found: exec: "gcc": executable f
    常见报错:Ccompiler"gcc"notfound:exec:"gcc":executablefilenotfoundin%PATH%一、背景操作系统:windows10专业版Go版本:goversiongo1.19.4windows/a......
  • go依赖管理
    原始依赖管理方式1.Go语言可以利用本身的能力做基础依赖管理,几个重要的组件包括GOPATH工作目录,Go命令工具(getinstallbuild)等,通过goget下载依赖包的最新版本到GOPATH......
  • 2023最新MongoDB规范
    前言MongoDB是非关系型数据库的典型代表,DB-EnginesRanking数据显示,近年来,MongoDB在NoSQL领域一直独占鳌头。MongoDB是为快速开发互联网应用而设计的数据库系统,其数据模......
  • Go语言中密码加密校验
    使用go自带的库bcryptbcrypt是不可逆的加密算法,无法通过解密密文得到明文。bcrypt和其他对称或非对称加密方式不同的是,不是直接解密得到明文,也不是二次加密比较密文,而是......
  • golang中的close函数
    close函数是用于关闭通道的。官方解释(摘自close函数源代码注释):Theclosebuilt-infunctionclosesachannel,whichmustbeeitherbidirectionalorsend-only.Itsho......
  • golang 日志
    packagelogimport( "NOONASN/global" "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" "path" "path/filepath")func......
  • 在Google的TPU上训练Fashion MNIST图像识别模型
    作者|张强今天我们要训练的模型是基于Keras框架,来训练FashionMNIST图像识别模型,该模型和MNIST是一样的分类数量。​​MNIST​​​的分类是0到9的十个数字​​​FashionMN......
  • Golang Slice
    Golang—SliceSlice是Go语言中的一种数据类型,又称动态数组,依托数组实现,可以方便的进行扩容、传递等,实际使用中比数组更灵活。实现原理Slice依托数组实现,底层数组对......